在C++中声明自定义的类之后,编译器会默认生成一些成员函数,这些函数被称为默认函数。包括构造函数、拷贝构造函数、拷贝赋值构造函数、移动构造函数、移动拷贝函数、析构函数。另外,编译器还会默认生成一些操作符函数,包括operator ,、operator &、operator &&、operator 、operator ->、operator ->、operator new、operator delete。
显式缺省函数 (= default)
但是如果实现了这些函数的自定义版本后,编译器就不会去生成默认的版本。有时候我们需要声明带参数的构造函数,此时就不会生成默认的构造函数,这样会导致类不再是POD类型(详见https://blog.csdn.net/WizardtoH/article/details/80767740),从而影响类的优化:
class Example {
public:
Example(int i) : data(i) {}
private:
int data;
};
int main()
{
std::cout << std::is_pod <Example>::value << std::endl; // 0
return 0;
}
C++11中提供了新的机制来控制默认函数生成来避免这个问题,我们在声明时在函数末尾加上”= default”来显式地指示编译器去生成该函数的默认版本,这样就保证了类还是POD类型:
class Example {
public:
Example() = default;
Example(int i) : data(i) {}
private:
int data;
};
int main()
{
std::cout << std::is_pod <Example>::value << std::endl; // 1
return 0;
}
显式删除函数 (= delete)
另一方面,有时候可能需要限制一些默认函数的生成,例如需要禁止拷贝构造函数的使用。原来,通过把拷贝构造函数声明为private成员,这样一旦使用编译器就会报错。而在C++11中,在韩式的定义或者声明后面加上”= delete”就能实现这个效果:
class Example {
public:
Example() = default;
Example(const Example& ex) = delete;
private:
int data;
};
int main()
{
Example ex;
Example ex1(ex); // 编译失败
return 0;
}
其他用法
= default和= delete 能够更加精准的控制类的默认函数版本。其中,= default同样也能在类外的定义中修饰成员函数:
class Example {
public:
Example() = default;
Example(const Example&);
private:
int data;
};
Example::Example(const Example& ex) = default;
这样的好处是,能够为一个类实现多个版本,只要我们在头文件里声明同样的函数,而在不同的cpp文件中用不同的方法实现,当选择不同的cpp编译时产生的版本就不同。
关于= delete,它不仅局限于限制生成默认函数,还能避免编译器做一些不必要的隐式数据转换:
class Example {
public:
Example(int i) {}
};
int main()
{
Example ex(1);
Example ex1('a'); // 编译成功,char会隐式装换成int型
return 0;
}
有时候我们不想要这种隐式转换:
class Example {
public:
Example(int i) {}
Example(char c) = delete;
};
int main()
{
Example ex(1);
Example ex1('a'); // 编译失败
return 0;
}
这个方法也能用于普通函数:
void func(int i) {}
void func(char c) = delete;
int main()
{
func(1);
func('a'); // 编译失败
return 0;
}
另外,还有很多用法,例如显示删除new操作符来避免在堆上分配对象、显示删除析构函数用于构建单例模式等等。