c++ basic
10 minute read ∼ Filed in : programming languageValgrind工具包
https://phenix3443.github.io/notebook/software-engineering/debug/valgrind-practices.html
常见错误
1. unordered_map
unordered_map 定义的key需要加一个template hash 方法,否则会报错:
implicit instantiation of undefined template 'std::__1::hash<KID>'
Cmake语法规则
https://blog.csdn.net/DinnerHowe/article/details/80455172
file
file(GLOB variable [RELATIVE path] [globbingexpressions]…)
GLOB 会产生一个由所有匹配globbing表达式的文件组成的列表,并将其保存到变量中。Globbing 表达式与正则表达式类似,但更简单。如果指定了RELATIVE 标记,返回的结果将是与指定的路径相对的路径构成的列表。 (通常不推荐使用GLOB命令来从源码树中收集源文件列表。
原因是:如果CMakeLists.txt文件没有改变,即便在该源码树中添加或删除文件,产生的构建系统也不会知道何时该要求CMake重新产生构建文件。globbing 表达式包括:
*.cxx - match all files with extension cxx
*.vt? - match all files with extension vta,...,vtz
f[3-5].txt - match files f3.txt,f4.txt, f5.txt
add_custom_target
添加一个目标,它没有输出;这样它就总是会被构建。
add_custom_target(Name [ALL] [command1 [args1...]]
[COMMAND command2 [args2...] ...]
[DEPENDS depend depend depend ... ]
[WORKING_DIRECTORY dir]
[COMMENT comment] [VERBATIM]
[SOURCES src1 [src2...]])
用Name选项给定的名字添加一个目标,这个目标会引发给定的那些命令。这个目标没有输出文件,并且总是被认为是过时的,即使那些命令试图去创建一个与该目标同名的文件。
使用ADD_CUSTOM_COMMAND命令可以生成一个带有依赖性的文件。默认情况下,没有目标会依赖于自定义目标。使用ADD_DEPENDENCIES命令可以添加依赖于该目标或者被该目标依赖的目标。如果指定了ALL选项,这表明这个目标应该被添加到默认的构建目标中,这样它每次都会被构建(命令的名字不能是ALL)。命令和选项是可选的;如果它们没有被指定,将会产生一个空目标。如果设定了WORKING_DIRECTORY参数,该命令会在它指定的路径下执行。如果指定了COMMENT选项,后跟的参数将会在构件的时候,在命令执行之前,被显示出来。DEPENDS选项后面列出来的依赖目标可以引用add_custom_command命令在相同路径下(CMakeLists.txt)生成的输出和文件。
C++知识点
import包
1,系统自带的头文件用尖括号括起来,这样编译器会在系统文件目录下查找。
#include
传参数
-
int, bool, float, double,
value
-
string, all others,
const &var_name;
-
xiu gai
*varname
namespace
c++ 4层作用域,文件,函数,类,复合语句。
如果不同文件定义了相同名字的类, 主函数include了2个文件后,调用类名会出错。引入namespace后,可以根据namespace: className:methodName 来调用。
peopleA.h:
namespace peopleA
class Student {
public:
...
}
peopleB.h:
namespace peopleB
class Student {
public:
...
}
Main.cpp
#include "peopleA.h"
#include "peopleB.h"
int main(){
peopleA:Student stud1();
peopleB:Studnet stud2();
return 0;
}
STD::move
string str1("hello");
string &&str2 = move(str1);
string &&str3 = move(str1);
cout << "str1: " << str1 << endl; // hello
cout << "str2: " << str2 << endl; // hello
cout << "str3: " << str3 << endl; // hello
string str4(move(str1));
cout << "str1: " << str1 << endl; //
cout << "str4: " << str4 << endl; // hello
explicit
explicit只能用于类内部的构造函数声明,加了后不允许隐式转换类型。这样可以在编译阶段给出错误,避免一些不必要的错误。
类型转换可以是隐式转换类型也可以是显示类型转换,
显式转换类型:
A a{1};
# a 从类型A到类型B
B b = static_cast<B>(a);
enum
枚举类型,一个类定义为枚举类型,那么他的值只能是当中取一个。
enum class TransactionState
{ GROWING, SHRINKING, COMMITTED, ABORTED };
inline
在系统下,栈空间(放置程序的局部数据, 也就是函数内数据, 的内存空间)是有限的,假如频繁大量的使用就会造成因栈空间不足而导致程序出错的问题,如,函数的死循环递归调用的最终结果就是导致栈内存空间枯竭。
为了解决一些频繁调用的小函数大量消耗栈空间(栈内存)的问题,特别的引入了 inline 修饰符,表示为内联函数。
在内部的工作就是在每个 for 循环的内部任何调用 callee(i)* 的地方都换成了 **(i%2>0)?1:2,这样就避免了频繁调用函数对栈内存重复开辟所带来的消耗。
inline 只适合函数体内代码简单的函数使用,不能包含复杂的结构控制语句例如 while、switch,并且不能内联函数本身不能是直接递归函数。
inline 函数仅仅是一个对编译器的建议,所以最后能否真正内联,看编译器的意思,它如果认为函数不复杂,能在调用点展开,就会真正内联,并不是说声明了内联就会内联,声明内联只是一个建议而已。
建议 inline 函数的定义放在头文件中
内联是以代码膨胀(复制)为代价,仅仅省去了函数调用的开销,从而提高函数的执行效率。
atomic
可以实现不显示加锁,多线程互斥访问同一个变量的目的。
Lambda表达式
格式
[capture list] (params list) mutable exception-> return type { function body }
capture list:捕获外部变量列表
params list:形参列表
mutable指示符:用来说用是否可以修改捕获的变量
exception:异常设定
return type:返回类型
function body:函数体
examples:
[](int a, int b) -> bool { return a < b; }
int a = 123;
auto f = [a] { cout << a << endl; };
f(); // 输出:123
int a = 123;
auto f = [=] { cout << a << endl; }; // 值捕获
f(); // 输出:123
int a = 123;
auto f = [&] { cout << a << endl; }; // 引用捕获
a = 321;
f(); // 输出:321
捕获形式 | 说明 |
---|---|
[] | 不捕获任何外部变量 |
[a,b,c..] | 默认以值得形式捕获指定的多个外部变量(用逗号分隔),如果引用捕获,需要显示声明(使用&说明符) |
[this] | 以值的形式捕获this指针 |
[=] | 以值的形式捕获所有外部变量 |
[&] | 以引用形式捕获所有外部变量 |
[=, &x] | 变量x以引用形式捕获,其余变量以传值形式捕获 |
[&, x] | 变量x以值的形式捕获,其余变量以引用形式捕获 |
Mutex锁
https://blog.csdn.net/faihung/article/details/88411839
https://blog.csdn.net/ktigerhero3/article/details/78249266
unordered_map 和map用法
https://blog.csdn.net/jingyi130705008/article/details/82633778
Map:
- 红黑树,数据有序,查询,插入,删除 O(logn)
- 自定义的key需要实现operator <
#include<iostream>
#include<map>
#include<string>
using namespace std;
int main()
{
// 构造函数
map<string, int> dict;
// 插入数据的三种方式
dict.insert(pair<string,int>("apple",2));
dict.insert(map<string, int>::value_type("orange",3));
dict["banana"] = 6;
// 判断是否有元素
if(dict.empty())
cout<<"该字典无元素"<<endl;
else
cout<<"该字典共有"<<dict.size()<<"个元素"<<endl;
// 遍历
map<string, int>::iterator iter;
for(iter=dict.begin();iter!=dict.end();iter++)
cout<<iter->first<<ends<<iter->second<<endl;
// 查找
if((iter=dict.find("banana"))!=dict.end()) // 返回一个迭代器指向键值为key的元素,如果没找到就返回end()
cout<<"已找到banana,其value为"<<iter->second<<"."<<endl;
else
cout<<"未找到banana."<<endl;
if(dict.count("watermelon")==0) // 返回键值等于key的元素的个数
cout<<"watermelon不存在"<<endl;
else
cout<<"watermelon存在"<<endl;
pair<map<string, int>::iterator, map<string, int>::iterator> ret;
ret = dict.equal_range("banana"); // 查找键值等于 key 的元素区间为[start,end),指示范围的两个迭代器以 pair 返回
cout<<ret.first->first<<ends<<ret.first->second<<endl;
cout<<ret.second->first<<ends<<ret.second->second<<endl;
iter = dict.lower_bound("boluo"); // 返回一个迭代器,指向键值>=key的第一个元素。
cout<<iter->first<<endl;
iter = dict.upper_bound("boluo"); // 返回一个迭代器,指向值键值>key的第一个元素。
cout<<iter->first<<endl;
return 0;
}
// 自定义key
struct person
{
string name;
int age;
person(string name, int age)
{
this->name = name;
this->age = age;
}
bool operator < (const person& p) const
{
return this->age < p.age;
}
};
map<person,int> m;
Unordered_map:
- 哈希表(开链法), 时间复杂度为O(1)
- key需要定义hash_value函数并且重载operator ==
#include<string>
#include<iostream>
#include<unordered_map>
using namespace std;
int main()
{
unordered_map<string, int> dict; // 声明unordered_map对象
// 插入数据的三种方式
dict.insert(pair<string,int>("apple",2));
dict.insert(unordered_map<string, int>::value_type("orange",3));
dict["banana"] = 6;
// 判断是否有元素
if(dict.empty())
cout<<"该字典无元素"<<endl;
else
cout<<"该字典共有"<<dict.size()<<"个元素"<<endl;
// 遍历
unordered_map<string, int>::iterator iter;
for(iter=dict.begin();iter!=dict.end();iter++)
cout<<iter->first<<ends<<iter->second<<endl;
// 查找
if(dict.count("boluo")==0)
cout<<"can't find boluo!"<<endl;
else
cout<<"find boluo!"<<endl;
if((iter=dict.find("banana"))!=dict.end())
cout<<"banana="<<iter->second<<endl;
else
cout<<"can't find boluo!"<<endl;
return 0;
}
condition_variable
cv.wait(lock, [] { return ready; }); 相当于:while (!ready) { cv.wait(lock); }
#include <iostream> // std::cout
#include <thread> // std::thread, std::this_thread::yield
#include <mutex> // std::mutex, std::unique_lock
#include <condition_variable> // std::condition_variable
std::mutex mtx;
std::condition_variable cv;
int cargo = 0;
bool shipment_available()
{
return cargo != 0;
}
// 消费者线程.
void consume(int n)
{
for (int i = 0; i < n; ++i) {
std::unique_lock <std::mutex> lck(mtx);
cv.wait(lck, shipment_available);
std::cout << cargo << '\n';
cargo = 0;
}
}
int main()
{
std::thread consumer_thread(consume, 10); // 消费者线程.
// 主线程为生产者线程, 生产 10 个物品.
for (int i = 0; i < 10; ++i) {
while (shipment_available())
std::this_thread::yield();
std::unique_lock <std::mutex> lck(mtx);
cargo = i + 1;
cv.notify_one();
}
consumer_thread.join();
return 0;
}