C++的类有四类特殊成员函数,它们分别是:默认构造函数、析构函数、拷贝构造函数、拷贝赋值运算符。如果实际编码时没有显示定义,那么编译器将会默认生成这四类成员函数。使用=default和=delete可以控制编译器默认函数体的使用。
1 =default
C++11新增了=default标识,编译器看到后,会生成默认的执行效率更高的函数定义体,同时会减轻编码时的工作量。当然,这里会引入一个问题,既然编译器会默认生成构造函数,那么=default的优势在哪里呢?回答这个问题之前,先看下这段代码
class Test{
public:
Test(int a):x(a){};
private:
int x;
};
int main ()
{
Test test;
return 0;
}
众所周知,上面这段是编译不过的,原因是因为在Test类中我们自己定义了一个构造函数,编译器看到后就不会再生成默认构造函数给我们,如果要解决这个编译问题的话需要我们提供一个没有参数的构造函数。如:
Test(){};
在类中加了上面的代码之后,编译器就会编译通过,但是在试想一下,如果这个类很大,且需要我们在类中初始化的成员很多呢?这个时候我们需要提供的这个默认构造函数就变得的很庞大,浪费我们很多时间进行变量的初始化,写一堆没有技术的赋值或者其它初始化语句。同样,拷贝构造函数和拷贝赋值函数也是一样。
=default就给我们提供了这样一个功能,加上之后,编译器就会给我们默认生成函数体,减轻工作量。上面的类就这可以这些写:
class Test{
public:
Test(int a):x(a){};
Test()=default;
private:
int x;
};
当然,=default不但可以在类成员内部添加也可以在类之外添加,但是使用=default时,必须遵守一个准则:default 函数特性只能用于类的特殊成员函数或者函数没有默认参数。=default写在类之外的方式如下:
class Test{
public:
Test(int a):x(a){};
Test()=default;
Test(const Test& ts);
Test& operator = (const Test& ts);
private:
int x;
};
Test::Test(const Test& ts) =default;
Test& Test::operator = (const Test& ts) =default;
上面的代码中演示了=default在类成员外部使用的场景。但是类中确没有析构函数,编码时,如果涉及到类的继承和派生,尤其是通过基类指针指向了派生类对象,当调用delete删除派生对象时,如果基类没有显示定义析构函数,编译器会为基类默认生成析构函数,基类对象会被正常释放,但是也会产生一个问题,派生类没有正确释放,可能会产生内存泄露等问题。正确解决这种问题的做法是在基类中显示定义一个虚析构函数。这种方法在C++11之前是我们解决这种问题经常使用的,但是C++11之后,可以使用=default,从而减轻我们的编码量,且编译器生成的代码效率更高。代码如下所示:
class Base{
public:
virtual ~Base()=default;
private:
int x;
};
class A : public Base{
private:
int y;
};
int main ()
{
Base *pBase = new A;
delete pBase;
return 0;
}
2 =delete
C++11之前,delete是和new配对使用的,释放程序在堆上开辟得空间,将资源返还给操作系统,C++11之后,delete又多了一个含义既:禁用成员函数的使用。使用方法为:在函数名称后面加上=delete。
下面代码定义了一个类,类里面定义了一个整型的成员变量,在main函数中使用时,创建了两个类的实例,一个传入参数用整型一个用浮点型,代码如下:
class Test {
public:
Test()=delete;
Test(int a):x(a) {std::cout<<x<<std::endl;}
private:
int x;
};
int main() {
Test test1(1);
Test test2(1.1);
return 0;
}
如上,这段代码是可以编译通过的,因为这段代码在编译时发生了隐式转换,将浮点型数据转成了整型,代价是损失了精度。代码运行后输出的结果都是:1。
如果不想在传入非整型的数据时编译通过,就可以使用=delete来抑制这种问题的产生。如使用=delete解决上面的问题,代码如下:
class Test {
public:
Test(int a):x(a) {std::cout<<x<<std::endl;}
Test(double)=delete;
private:
int x;
};
int main() {
Test test1(1);
Test test2(1.1);
return 0;
编译时报错信息为:
main.cpp:23:19: error: use of deleted function ‘Test::Test(double)’
23 | Test test2(1.1);
| ^
main.cpp:17:5: note: declared here
17 | Test(double)=delete;
| ^~~~
如上可知,使用=delete后,可以使我们禁用一些不需要编译器生成的默认函数,还可以避免因为数据类型原因导致的错误的函数调用。