=default、=delete、override

2024-08-29  本文已影响0人  hehehehe

在 C++11 及其后续版本中,= default 用于显式地指定编译器生成默认的构造函数、析构函数或拷贝/移动操作符。它允许你在类定义中明确地表示你希望使用编译器生成的默认实现,而不是自己手动实现这些函数。

语法

class MyClass {
public:
    MyClass() = default;             // 默认构造函数
    ~MyClass() = default;            // 默认析构函数
    MyClass(const MyClass&) = default; // 拷贝构造函数
    MyClass& operator=(const MyClass&) = default; // 拷贝赋值操作符
    MyClass(MyClass&&) = default;    // 移动构造函数
    MyClass& operator=(MyClass&&) = default; // 移动赋值操作符
};

使用场景

  1. 显式要求编译器生成默认函数:当你希望编译器生成默认的函数实现时,可以使用 = default。这在某些情况下是必须的,例如当你有其他的构造函数或析构函数时,编译器不会自动生成默认的构造函数或其他特殊成员函数。

    class MyClass {
    public:
        MyClass(int value) : value(value) {} // 自定义构造函数
    
        MyClass() = default; // 显式要求编译器生成默认构造函数
    
    private:
        int value;
    };
    
  2. 性能优化:有时编译器生成的默认实现比手动实现更高效,因为编译器可以直接生成内联代码。

  3. 确保特殊成员函数是 trivialconstexpr:使用 = default 可以保证某些情况下生成的特殊成员函数是 trivialconstexpr 的。

示例

以下是一个简单的示例,展示了如何使用 = default

#include <iostream>

class MyClass {
public:
    MyClass() = default; // 默认构造函数
    ~MyClass() = default; // 默认析构函数

    MyClass(const MyClass&) = default; // 拷贝构造函数
    MyClass& operator=(const MyClass&) = default; // 拷贝赋值操作符

    MyClass(MyClass&&) = default; // 移动构造函数
    MyClass& operator=(MyClass&&) = default; // 移动赋值操作符

    void display() const {
        std::cout << "MyClass instance" << std::endl;
    }
};

int main() {
    MyClass obj1; // 调用默认构造函数
    MyClass obj2 = obj1; // 调用拷贝构造函数
    MyClass obj3 = std::move(obj1); // 调用移动构造函数

    obj2 = obj3; // 调用拷贝赋值操作符
    obj3 = std::move(obj2); // 调用移动赋值操作符

    obj1.display();
    obj2.display();
    obj3.display();

    return 0;
}

在这个示例中,MyClass 使用 = default 显式要求编译器生成默认的构造函数、析构函数、拷贝构造函数、拷贝赋值操作符、移动构造函数和移动赋值操作符。

注意事项

通过使用 = default,你可以更清晰地表达你的意图,并且在某些情况下还能获得更好的性能和代码优化。

= delete

= delete 用于显式删除某个函数,表示该函数不可用。通常用于删除特殊成员函数,如拷贝构造函数、拷贝赋值操作符等,以防止对象被不正确地复制或赋值。

class MyClass {
public:
    MyClass() = default; // 默认构造函数
    MyClass(const MyClass&) = delete; // 删除拷贝构造函数
    MyClass& operator=(const MyClass&) = delete; // 删除拷贝赋值操作符
};

int main() {
    MyClass obj1;
    // MyClass obj2 = obj1; // 错误:拷贝构造函数被删除
    // MyClass obj3;
    // obj3 = obj1; // 错误:拷贝赋值操作符被删除
    return 0;
}

constexpr

constexpr 用于指定一个函数或变量可以在编译时求值。对于构造函数,constexpr 表示该构造函数可以用于生成常量表达式。

class MyClass {
public:
    constexpr MyClass(int value) : value(value) {}
    constexpr int getValue() const { return value; }
private:
    int value;
};

int main() {
    constexpr MyClass obj(42);
    static_assert(obj.getValue() == 42, "Value should be 42");
    return 0;
}

noexcept

noexcept 用于指定一个函数不会抛出异常。这在优化和异常安全性方面很有用。

class MyClass {
public:
    MyClass() noexcept = default;
    void doSomething() noexcept {
        // 不会抛出异常的代码
    }
};

int main() {
    MyClass obj;
    obj.doSomething();
    return 0;
}

override

override 用于显式指定一个虚函数覆盖了基类中的虚函数。这样可以帮助编译器检查函数签名是否匹配,防止因为签名不匹配而意外地创建了一个新的虚函数。

class Base {
public:
    virtual void doSomething() {}
};

class Derived : public Base {
public:
    void doSomething() override {
        // 覆盖基类中的虚函数
    }
};

final

final 用于指定一个类不能被继承,或者一个虚函数不能被进一步覆盖。

class Base {
public:
    virtual void doSomething() final {
        // 这个虚函数不能被覆盖
    }
};

class Derived final : public Base {
    // 这个类不能被继承
};

explicit

explicit 用于防止构造函数或转换运算符在不需要时隐式转换。这有助于避免意外的类型转换。

class MyClass {
public:
    explicit MyClass(int value) : value(value) {}
private:
    int value;
};

int main() {
    // MyClass obj = 42; // 错误:构造函数是 explicit 的
    MyClass obj(42); // 正确
    return 0;
}

总结

这些关键字和特性提供了更细粒度的控制和更强的表达能力,使得代码更加清晰、健壮和高效。通过合理使用这些关键字和特性,你可以编写出更易于维护和理解的代码。

上一篇下一篇

猜你喜欢

热点阅读