c++关键字default和deleted

1)C++入门级小知识,分享给将要学习或者正在学习C++开发的同学。

2)内容属于原创,若转载,请说明出处。

3)提供相关问题有偿答疑和支持。

default关键字:
背景问题:
C++ 的类有四类特殊成员函数,它们分别是:默认构造函数、默认析构函数、默认拷贝构造函数以及默认拷贝赋值运算符。
这些类的特殊成员函数负责创建、初始化、销毁,或者拷贝类的对象。如果程序员没有显式地为一个类定义某个特殊成员函数,
而又需要用到该特殊成员函数时,则编译器会隐式的为这个类生成一个默认的特殊成员函数。


清单1:
class X{
private:
int a;
};
X x;


在清单 1 中,程序员并没有定义类 X 的默认构造函数,但是在创建类 X 的对象 x 的时候,又需要用到类 X 的默认构造函数,此时,编译器会隐式的为类 X 生成一个默认构造函数。
该自动生成的默认构造函数没有参数,包含一个空的函数体,即 X::X(){ }。虽然自动生成的默认构造函数仅有一个空函数体,但是它仍可用来成功创建类 X 的对象 x,清单 1 也可以编译通过。

但是,如果程序员为类 X 显式的自定义了非默认构造函数,却没有定义默认构造函数的时候,清单 2 将会出现编译错误:


清单2:
class X{
public:
X(int i){
   a = i;
}     
private:
int a;
};
X x;  // 错误 , 默认构造函数 X::X() 不存在


清单 2 编译出错的原因在于类 X 已经有了用户自定义的构造函数,所以编译器将不再会为它隐式的生成默认构造函数。
如果需要用到默认构造函数来创建类的对象时,程序员必须自己显式的定义默认构造函数。例如:


清单 3
class X{
public:
X(){};  // 手动定义默认构造函数
X(int i){
   a = i;
}     
private:
int a;
};
X x;   // 正确,默认构造函数 X::X() 存在

从清单 3 可以看出,原本期望编译器自动生成的默认构造函数需要程序员手动编写了,即程序员的工作量加大了。
此外,手动编写的默认构造函数的代码执行效率比编译器自动生成的默认构造函数低。类的其它几类特殊成员函数也和默认构造函数一样,
当存在用户自定义的特殊成员函数时,编译器将不会隐式的自动生成默认特殊成员函数,而需要程序员手动编写,加大了程序员的工作量。
类似的,手动编写的特殊成员函数的代码执行效率比编译器自动生成的特殊成员函数低。


Defaulted 函数的提出
为了解决如清单 3 所示的两个问题:1. 减轻程序员的编程工作量;2. 获得编译器自动生成的默认特殊成员函数的高的代码执行效率,C++11 标准引入了一个新特性:defaulted 函数。
程序员只需在函数声明后加上“=default;”,就可将该函数声明为 defaulted 函数,编译器将为显式声明的 defaulted 函数自动生成函数体。例如:

清单 4
class X{
public:
X()= default;
X(int i){
   a = i;
}     
private:
int a;
};
X x;


在清单 4 中,编译器会自动生成默认构造函数 X::X(){},该函数可以比用户自己定义的默认构造函数获得更高的代码效率。

Defaulted 函数特性仅适用于类的特殊成员函数,且该特殊成员函数没有默认参数。
如下是错误的:
class X {
public:
int f() = default;      // 错误 , 函数 f() 非类 X 的特殊成员函数
X(int) = default;       // 错误 , 构造函数 X(int, int) 非 X 的特殊成员函数
X(int = 1) = default;   // 错误 , 默认构造函数 X(int=1) 含有默认参数
};

Defaulted 函数既可以在类体里(inline)定义,也可以在类体外(out-of-line)定义。例如:


清单 5
class X{
public:  
  X() = default; //Inline defaulted 默认构造函数
  X(const X&);
  X& operator = (const X&);
  ~X() = default;  //Inline defaulted 析构函数
};
X::X(const X&) = default;  //Out-of-line defaulted 拷贝构造函数
X& X::operator = (const X&) = default;     //Out-of-line defaulted // 拷贝赋值操作符


另外的一个问题是,我们知道在多态的时候往往将基类的析构函数声明为虚函数,目的是防止内存泄漏:
class X {
public:
virtual ~X(){};     // 手动定义虚析构函数
private:
int x;
};
class Y: public X {
private:
int y;
};
int main(){
X* x = new Y;
delete x;
}
程序员需要手动的编写基类的虚构函数的定义(哪怕函数体是空的),增加了程序员的编程工作量。更值得一提的是,手动定义的析构函数的代码执行效率要低于编译器自动生成的析构函数。
为了解决上述问题,我们可以将基类的虚析构函数声明为 defaulted 函数,这样就可以显式的指定编译器为该函数自动生成函数体。例如:
class X {
public:
virtual ~X()= defaulted; // 编译器自动生成 defaulted 函数定义体
private:
int x;
};
class Y: public X {
private:
int y;
};
int main(){
X* x = new Y;
delete x;
编译器会自动生成虚析构函数 virtual X::X(){},该函数比用户自己定义的虚析构函数具有更高的代码执行效率。

deleted关键字:
背景问题:
对于 C++ 的类,如果程序员没有为其定义特殊成员函数,那么在需要用到某个特殊成员函数的时候,编译器会隐式的自动生成一个默认的特殊成员函数,
比如拷贝构造函数,或者拷贝赋值操作符。例如:

清单 10
class X{
public:
X();
};
int main(){
X x1;
X x2=x1;   // 正确,调用编译器隐式生成的默认拷贝构造函数
X x3;
x3=x1;     // 正确,调用编译器隐式生成的默认拷贝赋值操作符
}


在清单 10 中,程序员不需要自己手动编写拷贝构造函数以及拷贝赋值操作符,
依靠编译器自动生成的默认拷贝构造函数以及拷贝赋值操作符就可以实现类对象的拷贝和赋值。这在某些情况下是非常方便省事的,
但是在某些情况下,假设我们不允许发生类对象之间的拷贝和赋值,可是又无法阻止编译器隐式自动生成默认的拷贝构造函数以及拷贝赋值操作符,那这就成为一个问题了。

Deleted 函数的提出
为了能够让程序员显式的禁用某个函数,C++11 标准引入了一个新特性:deleted 函数。程序员只需在函数声明后加上“=delete;”,就可将该函数禁用。
例如,我们可以将类 X 的拷贝构造函数以及拷贝赋值操作符声明为 deleted 函数,就可以禁止类 X 对象之间的拷贝和赋值。

清单 11
class X{            
     public:
       X();
       X(const X&) = delete;  // 声明拷贝构造函数为 deleted 函数
       X& operator = (const X &) = delete; // 声明拷贝赋值操作符为 deleted 函数
     };
int main(){
  X x1;
  X x2=x1;   // 错误,拷贝构造函数被禁用
  X x3;
  x3=x1;     // 错误,拷贝赋值操作符被禁用
}


在清单 11 中,虽然只显式的禁用了一个拷贝构造函数和一个拷贝赋值操作符,但是由于编译器检测到类 X 存在用户自定义的拷贝构造函数和拷贝赋值操作符的声明,
所以不会再隐式的生成其它参数类型的拷贝构造函数或拷贝赋值操作符,也就相当于类 X 没有任何拷贝构造函数和拷贝赋值操作符,所以对象间的拷贝和赋值被完全禁止了。
这种情况一般会通过一个宏定义的形式类实现,参考之前的c++笔记,关于使用如下宏定义实现:
DISALLOW_COPY_AND_ASSIGN

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值