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

C++ 构造函数语义学

构造函数是C++中用于初始化对象的特殊成员函数,其语义学涉及编译器的处理方式、对象的构造过程以及与内存管理的交互。以下是C++构造函数语义学的关键方面:

1. 构造函数的分类

默认构造函数

  • 当类中没有定义任何构造函数时,编译器会生成一个默认构造函数
  • 如果用户定义了任何构造函数,编译器不会生成默认构造函数(除非显式使用= default
    class MyClass {
    public:MyClass() {} // 默认构造函数
    };

拷贝构造函数

class MyClass {
public:MyClass(const MyClass& other) { /* 拷贝逻辑 */ }
};

 用于通过同类型的另一个对象初始化新对象

  • 形式为X(const X&)X(X&)
  • 如果用户没有定义,编译器会生成一个默认的成员逐个拷贝的拷贝构造函数

移动构造函数 (C++11)

class MyClass {
public:MyClass(MyClass&& other) noexcept { /* 移动资源 */ }
};

 用于"移动"资源而不是拷贝资源

  • 形式为X(X&&)
  • 如果用户没有定义,且类满足特定条件,编译器会生成默认的移动构造函数

转换构造函数

  • 接受单个参数的构造函数,可用于隐式类型转换
  • 可以使用explicit关键字禁止隐式转换
class MyClass {
public:explicit MyClass(int) {} // 禁止隐式转换
};

=default 和 =delete

class MyClass {
public:MyClass() = default; // 显式要求编译器生成默认实现MyClass(const MyClass&) = delete; // 禁止拷贝
};
  • =default:显式要求编译器生成默认实现

  • =delete:禁止特定操作(如拷贝构造)

2. 构造函数的调用语义

直接初始化 vs 拷贝初始化

MyClass obj1; // 默认构造
MyClass obj2(); // 函数声明,不是构造!
MyClass obj3{}; // 值初始化
MyClass obj4(arg); // 直接初始化
MyClass obj5 = arg; // 拷贝初始化(可能被优化)
MyClass obj6 = {arg}; // 列表初始化

继承中的构造函数

  • 派生类构造函数在初始化列表中调用基类构造函数
  • 如果没有显式调用,会尝试调用基类的默认构造函数
class Base {
public:Base(int) {}
};class Derived : public Base {
public:using Base::Base; // 继承Base的构造函数 (C++11)// 或者显式调用基类构造函数Derived(int x) : Base(x) {}
};

3. 构造函数的实现语义

编译器优化

  • 返回值优化(RVO):编译器可能消除临时对象的构造
  • 命名返回值优化(NRVO):对命名返回值的优化

构造函数的异常处理

  • 如果构造函数抛出异常,已构造的成员和基类子对象会被自动析构
  • 资源管理类(RAII)在构造函数异常时仍能保证资源释放

4. 特殊成员函数的生成规则 (C++11起)

class Example {
public:
// 用户声明或删除以下任一特殊成员函数会影响其他函数的生成
Example(); // 默认构造函数
Example(const Example&); // 拷贝构造函数
Example(Example&&); // 移动构造函数
Example& operator=(const Example&); // 拷贝赋值
Example& operator=(Example&&); // 移动赋值
~Example(); // 析构函数
};

生成规则(C++17):

  1. 默认构造函数:仅当类没有用户声明的构造函数时生成
  2. 析构函数:总是生成(除非用户声明了删除的析构函数)
  3. 拷贝构造函数:仅当类没有用户声明的移动操作或析构函数时生成
  4. 拷贝赋值运算符:同上
  5. 移动构造函数和移动赋值运算符:仅当类没有用户声明的拷贝操作或析构函数时生成

5. 构造函数的内存语义

对象构造过程

  1. 分配内存(栈或堆)
  2. 初始化虚表指针(如果有多态)
  3. 按声明顺序构造基类子对象
  4. 按声明顺序构造成员变量
  5. 执行构造函数体

成员初始化顺序

  • 成员变量总是按照它们在类中声明的顺序初始化
  • 初始化列表中的顺序不影响实际初始化顺序

6. 现代C++中的构造函数改进

委托构造函数 (C++11)

class MyClass {
public:MyClass() : MyClass(0, 0) {} // 委托给另一个构造函数MyClass(int a, int b) : a_(a), b_(b) {}
private:int a_, b_;
};
  • 一个构造函数可以调用同类的另一个构造函数

  • 避免代码重复

继承构造函数 (C++11)

class Base {
public:Base(int) {}
};class Derived : public Base {
public:using Base::Base; // 继承Base的构造函数 (C++11)// 或者显式调用基类构造函数Derived(int x) : Base(x) {}
};
  • 派生类必须初始化其直接基类

  • C++11允许使用using声明继承基类构造函数

列表初始化 (C++11)

class Point {
public:
int x, y;
Point(int a, int b) : x(a), y(b) {}
Point(std::initializer_list<int> init) {
auto it = init.begin();
x = *it++;
y = *it;
}
};Point p1{1, 2}; // 调用initializer_list构造函数
Point p2(1, 2); // 调用常规构造函数

构造顺序

  1. 基类构造函数(按继承顺序)

  2. 成员变量构造函数(按声明顺序)

  3. 构造函数体执行

注意事项

  1. 虚函数在构造函数中调用的是当前类的实现,不是派生类的

  2. 构造函数不应调用虚函数来实现多态行为

  3. 构造函数可以抛出异常,但需要妥善处理资源

  4. RAII(资源获取即初始化)是构造函数的重要应用模式

理解这些构造函数语义有助于编写更安全、更高效的C++代码,特别是在涉及资源管理、对象生命周期和

http://www.lryc.cn/news/607022.html

相关文章:

  • Context API
  • 【AI论文】具备测试时扩散能力的深度研究者
  • win11怎么看本机ip地址?怎么查看代理端口?
  • leetcode 118. 杨辉三角 简单
  • 【C#学习Day14笔记】泛型、集合(数组列表Arraylist、列表list)与字典
  • 基于单片机汽车少儿安全预警系统
  • 118. 杨辉三角
  • 数据结构:在链表中查找(Searching in a Linked List)
  • [ java 网络 ] TPC与UDP协议
  • NTC热敏电阻的原理及应用
  • 8.1 开始新的学习历程
  • 应急响应(windows工具版)
  • Java文件读写I/O操作教程
  • Mysql group by
  • 【C++篇】C++11入门:踏入C++新世界的大门
  • 国内用户如何用手机进行YouTube直播?
  • 『React』 组件通信全攻略
  • 如何从头开始搭建属于自己的家用nas实现内网穿透访问
  • 提升文档管理:推荐一键Docker部署的全文索引搜索引擎工具
  • 如何将联系人从三星手机转移到 iPhone
  • RabbitMQ-镜像队列(Mirrored Queues)
  • 测试平台如何重塑CI/CD流程中的质量协作新范式
  • 什么是CI/CD?
  • 层次聚类:无需“猜”K值,如何让数据自己画出“家族图谱”?
  • HQChart实战教程58:K线主图仿TradingView实现
  • 日志归档存储策略在海外云服务器环境的容量规划方法
  • Bootstap Vue 之b-form-radio-group 不显示选中状态问题
  • Web学习:SQL注入之联合查询注入
  • 《协作画布的深层架构:React与TypeScript构建多人实时绘图应用的核心逻辑》
  • 《React Router深解:复杂路由场景下的性能优化与导航流畅性构建》