当前位置: 首页 > article >正文

[C++11] 类中新特性的添加

默认的移动构造和移动赋值

在 C++11 之前,编译器会为每个类自动生成默认的构造函数、析构函数、拷贝构造函数、拷贝赋值运算符等函数,以实现对象的创建、销毁和拷贝操作。但拷贝操作会复制整个对象的数据,效率低,尤其是在处理大对象或动态分配的资源时。为了解决这一问题,C++11 引入了移动语义(Move Semantics),并提供了两个新的默认函数:移动构造函数移动赋值运算符

移动构造和移动赋值运算符的作用是通过“转移”资源来代替“复制”资源,提高效率。它们通常用于避免不必要的拷贝操作,尤其是当资源的原有所有者不再需要保留这些资源时。

生成规则

  • 若类未定义析构函数、拷贝构造函数、拷贝赋值运算符或移动构造函数,编译器会自动生成默认的移动构造和移动赋值运算符。
  • 若类包含自定义析构函数、拷贝构造函数或拷贝赋值运算符,则编译器不会自动生成移动构造和移动赋值运算符,除非显式指定 =default

移动构造函数和移动赋值的行为

  • 内置类型成员将按字节逐一拷贝。
  • 自定义类型成员会优先调用它们的移动构造函数(若存在),否则调用拷贝构造函数。

也就是说,与其他的默认函数一样,自定义类型仍然需要看本身有没有实现相应的函数。

示例代码
#include <string>
#include <utility> // 包含std::move

class Person {
public:
    Person(const char* name = "", int age = 0)
        : _name(name), _age(age) {}

// 移动构造函数自动生成,因为未定义拷贝构造或赋值函数

private:
    std::string _name;
    int _age;
};

int main() {
    Person p1("Alice", 25);
    Person p2 = std::move(p1); // 触发移动构造
    
    return 0;
}

在上述代码中,std::movep1 转换为右值引用(rvalue reference),指示编译器可以“转移”p1 的资源,而无需拷贝。p1 的数据会被移动到 p2,此操作避免了 p1 的拷贝过程。

注意

若类定义了移动构造函数或移动赋值运算符,编译器不会再自动生成拷贝构造函数和拷贝赋值运算符。

声明时给缺省值

在 C++11 之前,默认参数值只能在函数声明中给出,不能直接在成员变量定义时赋值。而 C++11 允许在类的成员变量声明时直接赋默认值,这一特性提高了代码的简洁性,并增强了初始化的灵活性。这样,在构造对象时,若未传入对应参数,成员变量会自动采用声明时指定的默认值。

示例代码

class Person {
public:
    Person(const char* name = "") : _name(name) {}

private:
    std::string _name = "DefaultName"; // 直接在声明中赋缺省值
    int _age = 18; // 默认值为 18
};

int main() {
    Person p; // _name 初始化为 "DefaultName",_age 初始化为 18
    
    return 0;
}

通过在声明时赋值,减少了构造函数中初始化的代码量,避免重复设置默认值,提高了可读性。

defaultdelete

C++11 引入了 =default=delete,提供更精细的控制:

  • =default:当类中定义了某个自定义函数(如拷贝构造),编译器不会自动生成移动构造函数。若希望保留自动生成的行为,可使用 =default 显式要求编译器生成该函数。
  • =delete:通过 =delete,可以禁用类的某些默认行为(如拷贝或赋值),例如禁用拷贝构造可以避免误用拷贝构造函数带来的资源分配问题。

示例代码:

class Person {
public:
Person(const char* name = "", int age = 0)
: _name(name), _age(age) {}

Person(const Person& p) = delete; // 禁用拷贝构造函数
Person(Person&& p) = default;      // 显式要求生成默认移动构造函数

private:
std::string _name;
int _age;
};

int main() {
    Person s1("Alice", 25);
    // Person s2 = s1; // 错误:拷贝构造函数被禁用
    Person s3 = std::move(s1); // 调用默认的移动构造函数
    return 0;
}

finaloverride

在 C++ 的继承和多态中,派生类可能会误写或错写基类的虚函数,导致未按预期覆盖基类的行为。C++11 提供了 finaloverride 关键字,帮助开发者更好地控制和检测继承链中的函数覆盖行为。

  • override:用于修饰派生类中的虚函数,表示这是对基类中同名虚函数的覆盖。如果函数签名不匹配,编译器会报错。
  • final:用于修饰类或虚函数,表示该类或虚函数不允许被进一步继承或重写。
示例代码
class Base {
public:
    virtual void display() const {
        std::cout << "Base class display" << std::endl;
    }

    virtual void show() const final { // 不允许派生类重写此函数
        std::cout << "Base class show" << std::endl;
    }
};

class Derived : public Base {
public:
    void display() const override { // 正确覆盖基类的display
        std::cout << "Derived class display" << std::endl;
    }
    // void show() const override; // 错误:不能重写final函数
};

// class FurtherDerived : public Derived final {}; // 不允许进一步派生

override 关键字帮助避免虚函数重写错误,final 关键字则能阻止类或函数的进一步派生和重写,提升代码安全性和可读性。


http://www.kler.cn/a/387754.html

相关文章:

  • 如何使用IDEA创建Maven/SSM工程?
  • 移门缓冲支架的作用与优势
  • Redis五种数据类型剖析
  • uniapp的基本使用(easycom规范和条件编译)和uview组件的安装和使用
  • 异步提交Django
  • 【Linux】常用命令(2.6万字汇总)
  • 网页版五子棋——匹配模块(服务器端开发)
  • 梧桐数据库与GBase日期函数比较
  • C++ 越来越像函数式编程了!
  • linux devfreq 模块
  • flink 内存配置(五):网络缓存调优
  • video素材格式转换--mp4转webm(vue3+Nodejs)
  • 如何运营Github Org
  • Hunyuan-Large:推动AI技术进步的下一代语言模型
  • 刘艳兵-DBA027-在Oracle数据库,通常可以使用如下方法来得到目标SQL的执行计划,那么通过下列哪些方法得到的执行计划有可能是不准确的?
  • 鸿蒙next版开发:ArkTS组件自定义事件拦截详解
  • 腾讯混元3D模型Hunyuan3D-1.0部署与推理优化指南
  • 【PGCCC】Postgresql LWLock 原理
  • 孤岛的总面积(Dfs C#
  • IP系列之scan讨论
  • Java学习篇之JVM 调优
  • Hive 的数据类型
  • 分布式中常见的问题及其解决办法
  • BUG: scheduling while atomic
  • 软考中级 软件设计师 上午考试内容笔记(个人向)Part.2
  • 道品科技智慧农业中的自动气象检测站