用=default与=delete控制特殊成员函数
一、=default:默认实现
=default关键字用于显式要求编译器生成一个默认的特殊成员函数,包括构造函数、析构函数、拷贝构造函数和拷贝赋值运算符。
当我们定义了自己的构造函数或析构函数时,编译器就不会自动生成默认构造函数或析构函数。但如果我们想要保留这些默认的行为,可以使用=default来显式要求编译器生成。
class Example
{
public:
Example() = default; // 显式要求编译器生成默认构造函数
Example(const Example&) = default; // 显式要求编译器生成拷贝构造函数
Example& operator=(const Example&) = default; // 显式要求编译器生成拷贝赋值运算符
~Example() = default; // 显式要求编译器生成析构函数
};
在上面的代码中,我们使用了=default来显式要求编译器生成默认构造函数、拷贝构造函数、拷贝赋值运算符和析构函数。这样,即使我们定义了其他构造函数或析构函数,这些默认的特殊成员函数仍然会被生成。
二、=delete:禁用函数
=delete关键字用于显式禁止编译器生成一个特殊成员函数。当我们不希望某个特殊成员函数被使用时,可以使用=delete来显式禁止编译器生成。
class Example
{
public:
Example(int value) : value_(value) {}
Example(const Example&) = delete; // 禁止编译器生成拷贝构造函数
Example& operator=(const Example&) = delete; // 禁止编译器生成拷贝赋值运算符
private: int value_;
};
在上面的代码中,我们使用了=delete来禁止编译器生成拷贝构造函数和拷贝赋值运算符。这意味着我们不能使用这些函数来创建或赋值Example类的对象。
三、代码解释说明
现在,让我们通过一个具体的例子来解释=default和=delete的用法。
#include <iostream>
class Example {
public:
Example() = default;
Example(int value) : value_(value) {}
Example(const Example&) = delete;
Example& operator=(const Example&) = delete;
~Example() = default;
void printValue() const {
std::cout << "Value: " << value_ << std::endl;
}
private:
int value_;
};
int main() {
Example e1(42);
e1.printValue();
// Example e2(e1); // 编译错误,拷贝构造函数被禁止
// e1 = Example(24); // 编译错误,拷贝赋值运算符被禁止
return 0;
}
在这个例子中,我们定义了一个名为Example的类,它有一个默认构造函数、一个接受整数参数的构造函数、一个拷贝构造函数和一个拷贝赋值运算符。
我们使用了=default来显式要求编译器生成默认构造函数和析构函数,同时使用了=delete来禁止编译器生成拷贝构造函数和拷贝赋值运算符。
在main函数中,我们创建了一个Example类的对象e1,并调用了它的printValue函数来打印其内部值。由于拷贝构造函数和拷贝赋值运算符被禁止,我们不能使用这些函数来创建或赋值Example类的对象。如果我们尝试这样做,编译器会报错。
四、实际应用场景
单例模式(Singleton Pattern):在单例模式中,我们希望类只能有一个实例,并且禁止被复制。这时候,我们可以使用=delete来禁止拷贝构造函数和赋值操作符,从而确保单例的唯一性。
移动语义(Move Semantics):移动语义可以大大提高性能。但有时候我们可能不希望某些类被移动。通过=delete,我们可以禁止移动构造函数和移动赋值操作符的生成,确保我们的类不会被意外地移动。
虚函数(Virtual Functions):有时候我们希望某些类不被继承,可以将其析构函数声明为纯虚函数并=delete,从而禁止其被继承。
五、总结
=default和=delete这两个关键字的运用,为我们的代码注入了更多的灵活性和可控性。通过使用这两个关键字,我们能够更加精确地定义我们的类的行为,使得我们的代码更加健壮、简洁和易于理解。
2868

被折叠的 条评论
为什么被折叠?



