18.调试与断言
常用调试宏
#include <assert.h>
cout << __FILE__ << endl;
cout << __LINE__ << endl;
cout << __TIME__ << endl;
cout << __FUNCTION__ <<endl;
静态断言static_assert()若不满足条件语句,则在编译时就报错,
assert()运行时报错
19.R与转义字符
在字符串前加上R可以避免缺失转义字符引起的各种稀奇古怪的现象
string path = R"E:\QtProjects\fplayer";
20.宽字符
wchar_t * p 的输出应该使用std::wcout减少出错概率
21.空指针的二义性:应使用nullptr初始化空指针
NULL是宏定义出来的,本质为int 0,因此把NULL当做空指针使用时会有二义性,比如:
void go(int); //函数1
void go(void*); //函数2
go(NULL); //会调用函数2,而不是函数1
22.auto与内部函数绑定
仿函数:一个函数指针,指向类内部公有函数
#include <functional>
using namespace std::placeholders
struct A{
int add(int);
int add2(int,int);
int add3(int,int,int);
}
A a;
auto func = bind(&A::add,&a,_1);
auto func = bind(&A::add2,&a,_1,_2);
auto func = bind(&A::add3,&a,_1,_2,_3);
23.mutable突破const成员函数限定
使用mutable修饰成员变量,就可以在const成员函数中对该变量进行修改
24.std::function函数包装器
#include <functional>
void func(void);
class A{
public:int func(int){
}
};
class B{
public:operator ()(int){
}
};
std::function<void(void)> fptr1 = func;
fptr1();
std::function<int(int)> fptr2 = A::func;
fptr2(100);
B b;
fptr2 = b;
fptr2(100);
25.lambda表达式的工程意义和延迟加载细节
lambda表达式帮助c++实现了在需要的时候随时定义函数,就像在90c之前,c++无法随时定义变量,但90c之后可以,而现在有了lambda,c++可以随时定义函数
lambda表达式的延迟调用:
int a = 0;
auto fun1 = [=](){
return a;};
auto fun2 = [&](){
return a;};
a += 1;
cout << fun1() << endl; //输出0,实现了延迟调用
cout << fun2() << endl; //输出1,捕获的是引用
auto fun3 = [=](){
return ++a;}; //报错,通过=捕获的变量只读
auto fun3 = [=]()mutable{
return ++a;}; //想要修改可以添加mutable属性
cout << fun3() << endl; //输出2,成功自增
26.简易内存泄露检测设计
1.构建一个链表结构体_MemoryList用于保存每一次new的信息(size,FILE ,__LINE__等)
2.重载new,new[], delete,delete[],使得每次调用这些函数都会往_MemoryList中添加或者删除一个保存该次内存分配信息的节点
3.实现MemLeakDetec退出程序前调用,遍历_MemoryList,即可确认是否内存泄露,以及定位内存泄露的位置
27.对象构建的时间开销检测
#include <sys/time.h>
clock_t startTime = clock();
//content
clock_t elapsed = clock() - startTime; //单位CPU时钟
28.可变参数模板和singleton
参数类型后跟…
template<class... T>
void func(T... args){
cout << sizeof...(args) << endl; //打印参数个数
}
func();
func(1);
func(1,2.5,"");
template<typename T>
class Singleton{
public:template<typename... Args>static T* Instance(Args&&... args){
//参数完美转发if(m_pInstance == nullptr){
m_pInstance = new T(std::forward<Args>args...);//参数完美转发}return m_pInstance;}
private:Singleton(void){
}Singleton(const Singleton&){
}Singleton& operator =(const Singleton&){
}
private:static T* m_pInstance;
};
struct CLASSA{
CLASSA(){
cout<<"class A"<<endl;}
};
struct CLASSB{
CLASSB(int x){
cout<<"classB"<<endl;}
};
struct CLASSC{
CLASSC(int x,double y){
cout<<"class C"<<endl;}
};
template<class T>T* Singleton<T>::m_pInstance = nullptr;
int main()
{
Singleton<CLASSA>::Instance();Singleton<CLASSB>::Instance(1);Singleton<CLASSC>::Instance(1,3.14);cin.get();return 0;
}
29.shared_ptr与weak_ptr
shared_ptr 不能用c++原生的赋值初始化构建
#include <memory>
shared_ptr<int> p(new int); //正确
shared_ptr<int> p = new int; //编译器报错
当遇到shared_ptr循环引用的清况,把其中一个shared_ptr换成weak_ptr就能正常析构,因为weak_ptr不操作资源,不会增加引用计数。
30.拷贝构造和移动构造
编译器默认拷贝构造是浅拷贝,若类内有指针,则只拷贝指针的值,比如用B拷贝构造A,若B析构了,delete掉自己的指针,则A当中的指针失效,这种情况一定要自定义拷贝构造函数
某些情况下,用体积较大的复杂自定义类型的临时变量进行拷贝构造,则开销太大,可以使用右值移动构造函数,&&
31.字节对齐
struct test{
char a;
int b;
};
cout << offsetof(test,a) << endl; // 0
cout << offsetof(test,b) << endl; // 4
alignof alignas
struct test{
double a;
double b;
double c;
double d;
};
cout << alignof(test) << endl; //字节对齐方式为8字节
=============================
struct alignas(32) test{
double a;
double b;
double c;
double d;
};
cout << alignof(test) << endl; //字节对齐方式改为32字节,能提高内存访问效率