一、默认函数
C++默认会实现一些函数,其中类成员函数有:
- 构造函数
- 析构函数
- 拷贝构造
- 赋值函数(=)
- 移动构造
- 移动赋值
以及一些全局操作函数:
- operator,
- operator&
- operator&&
- operator*
- operator->
- operator->*
- operator new
- operator delete
二、=default
显示缺省,对于默认生成的函数而言,并不是默认所有都会生成,而是在需要时才会生成。而且当用户自己实现其中的某个函数时,编译器就会移除相应的默认函数,这样就会导致类变得不是POD了。
C++11提供了=default关键字,用于默认函数定义之后,表示要求编译器生成对应的默认函数,从而确保类的POD。
class Empty
{
public:Empty() = default;Empty(int i) {}
};int main()
{std::cout << std::is_pod<Empty>::value << std::endl; //1
}
除了在类内指定default外,也可以在类外指定,但是类外指定依旧不是pod类型,类外指定的好处是:可以通过
class Empty
{
public:Empty() = default;Empty(int i) {}Empty& operator=(Empty&);
};
inline Empty& Empty::operator=(Empty&)=default;int main()
{std::cout << std::is_pod<Empty>::value << std::endl; //0
}
三、=delete
- 与default对应的还有显示删除delete表示,显示删除某个函数。最常见的例子就是单例中需要屏蔽拷贝、析构等函数,以前的做法是通过私有化不定义,但是还是可以被友元访问。
从C++11开始,可以直接在函数参数列表后添加=delete删除此函数,被删除的函数无法重载:
fun() = delete;
- 显示删除除了屏蔽函数之外,还可以用于抑制隐式类型转换:
class ConvType {
public:
ConvType(int i) {};
ConvType(char c) = delete; // 删除char版本
};
void Func(ConvType ct) {}
int main() {Func(3);Func('a'); // 无法通过编译ConvType ci(3);ConvType cc('a'); // 无法通过编译
}
- 而说到抑制隐式转换,还有一种方式是explicit关键字,当一个函数使用explicit和delete同时修饰时,反而会出现一些问题:
class ConvType {public:ConvType(int i) {};explicit ConvType(char c) = delete; // 删除explicit的char构造函数};void Func(ConvType ct) {}int main() {Func(3);Func('a'); // 可以通过编译ConvType ci(3);ConvType cc('a'); // 无法通过编译}
这里对于explicit ConvType(char c)函数而言,意味着拒绝从字符char隐式转换为ConvType类型,因此 Func('a');按explicit的本意是无法通过编译的,但是此处添加了=delete之后,反而移除了explicit的本意。
对比前面的ConvType(char c) = delete;而言,对于Func('a');则优先调用ConvType(char c)进行隐式类型转换,但是由于此函数已经被删除,所以无法构造函数。
因此通常不建议将explicit于delete关键字用于同一个函数上。
- delete除了应用于类成员函数外,还可以应用于普通函数:
void Func(int i){};void Func(char c) = delete; // 显式删除char版本int main(){Func(3);Func('c'); // 本句无法通过编译return 1;}// 编译选项:g++ -std=c++11 7-2-8.cpp
- 除此之外,还可以用于删除new函数,使得类只能创建于栈,而不能创建于堆上。
#include <cstddef>class NoHeapAlloc{public:void * operator new(std::size_t) = delete;};int main(){NoHeapAlloc nha;NoHeapAlloc * pnha = new NoHeapAlloc; // 编译失败return 1;}// 编译选项:g++ -std=c++11 7-2-9.cpp
- 以及删除析构函数,使得类只能创建于堆,而不能创建于栈上。(此时该对象是不需要被释放的(无法被释放))
#include <cstddef>#include <new>extern void* p;class NoStackAlloc{public:~NoStackAlloc() = delete;};int main(){NoStackAlloc nsa; // 无法通过编译new (p) NoStackAlloc(); // placement new, 假设p无需调用析构函数return 1;}// 编译选项:g++ 7-2-10.cpp -std=c++11-c