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

【C++ 类和数据抽象】构造函数

目录

一、构造函数的基本概念

1.1 构造函数核心特性

1.2 构造函数的作用

1.3 构造函数类型体系

二、构造函数的类型

2.1 默认构造函数

2.2 带参数的构造函数

2.3 拷贝构造函数

2.4 移动构造函数(C++11 及以后)

三、初始化关键技术

3.1 成员初始化列表

3.2 初始化顺序规则

四、构造函数的使用场景

4.1 对象的初始化

4.2 资源管理

4.3 对象的创建和初始化的封装

五、构造函数的初始化列表

5.1 语法和作用

5.2 初始化列表的优势

六、委托构造函数(C++11 及以后)

6.1 定义和作用

6.2 委托构造函数的优点

七、构造函数的注意事项

7.1 构造函数的重载

7.2 构造函数的异常处理

7.3 构造函数与析构函数的配合

八、总结

九、参考资料


在 C++ 面向对象编程中,构造函数扮演着至关重要的角色。它是一种特殊的成员函数,用于在创建对象时对对象进行初始化操作。构造函数确保对象在使用之前处于一个合理的状态,使得对象的创建和初始化过程更加安全和高效。

一、构造函数的基本概念

1.1 构造函数核心特性

构造函数是类对象的初始化入口,具有以下关键特征:

  • 自动调用:对象创建时自动执行

  • 无返回类型:与类同名,不声明返回类型

  • 重载能力:支持多个不同参数的版本

  • 初始化控制:负责成员变量初始化

class Clock {
public:// 默认构造函数Clock() : hour(0), minute(0), second(0) {}// 参数化构造函数Clock(int h, int m, int s) : hour(h), minute(m), second(s) {}// 拷贝构造函数Clock(const Clock& other): hour(other.hour), minute(other.minute), second(other.second) {}private:int hour;int minute;int second;
};

1.2 构造函数的作用

  • 初始化对象:为对象的数据成员赋予初始值,确保对象在创建后处于一个可用的状态。
  • 资源分配:在构造函数中可以进行一些资源的分配操作,如动态内存分配、打开文件等。

1.3 构造函数类型体系

类型语法形式调用时机
默认构造函数ClassName()默认初始化
参数化构造函数ClassName(params)显式传参初始化
拷贝构造函数ClassName(const ClassName&)对象拷贝时
移动构造函数ClassName(ClassName&&)对象移动时(C++11)
委托构造函数ClassName() : ClassName(0)构造函数复用(C++11)
转换构造函数ClassName(SingleParamType)隐式类型转换

二、构造函数的类型

2.1 默认构造函数

默认构造函数是一种不需要任何参数的构造函数。如果类中没有显式定义任何构造函数,编译器会自动生成一个默认构造函数。这个默认构造函数会对对象的数据成员进行默认初始化。例如:

#include <iostream>
using namespace std;class Point {
private:int x;int y;
};int main() {Point p; // 调用默认构造函数return 0;
}

但如果类中显式定义了其他构造函数,编译器将不会再自动生成默认构造函数。

2.2 带参数的构造函数

带参数的构造函数允许在创建对象时传递参数,从而为对象的数据成员赋予特定的初始值。例如:

#include <iostream>
using namespace std;class Rectangle {
private:int width;int height;
public:Rectangle(int w, int h) : width(w), height(h) {}int getArea() {return width * height;}
};int main() {Rectangle rect(5, 3); // 调用带参数的构造函数cout << "Area: " << rect.getArea() << endl;return 0;
}

2.3 拷贝构造函数

拷贝构造函数是一种特殊的构造函数,用于创建一个新对象,该对象是另一个同类型对象的副本。拷贝构造函数的参数通常是一个常量引用。例如:

#include <iostream>
using namespace std;class MyClass {
private:int data;
public:MyClass(int d) : data(d) {}MyClass(const MyClass& other) : data(other.data) {}int getData() {return data;}
};int main() {MyClass obj1(10);MyClass obj2(obj1); // 调用拷贝构造函数cout << "obj2 data: " << obj2.getData() << endl;return 0;
}

如果类中没有显式定义拷贝构造函数,编译器会自动生成一个浅拷贝的拷贝构造函数。但在涉及动态内存分配等情况时,可能需要自定义拷贝构造函数来实现深拷贝。

2.4 移动构造函数(C++11 及以后)

移动构造函数是 C++11 引入的一种新的构造函数,用于将一个临时对象(右值)的资源转移到新对象中,避免不必要的拷贝操作,提高性能。例如:

#include <iostream>
#include <utility>
using namespace std;class DynamicArray {
private:int* arr;int size;
public:DynamicArray(int s) : size(s) {arr = new int[size];}// 移动构造函数DynamicArray(DynamicArray&& other) noexcept : arr(other.arr), size(other.size) {other.arr = nullptr;other.size = 0;}~DynamicArray() {delete[] arr;}
};int main() {DynamicArray arr1(10);DynamicArray arr2(std::move(arr1)); // 调用移动构造函数return 0;
}

、初始化关键技术

3.1 成员初始化列表

初始化列表与赋值操作的对比:

class InitDemo {
public:// 初始化列表方式InitDemo(int a, double b) : m_a(a), m_b(b) {}// 赋值方式(效率较低)InitDemo(int a) {m_a = a;      // 先默认构造再赋值m_b = 0.0;    // 可能产生临时对象}private:int m_a;double m_b;const int MAX = 100;  // 必须使用初始化列表
};

3.2 初始化顺序规则

成员初始化顺序由声明顺序决定,与初始化列表顺序无关:

class OrderDemo {
public:// 警告:初始化顺序与声明顺序不一致OrderDemo(int x) : b(x), a(b) {}  // a被初始化为未定义的b值private:int a;int b;
};

四、构造函数的使用场景

4.1 对象的初始化

构造函数最主要的用途就是对对象进行初始化。通过构造函数,可以确保对象在创建后立即拥有合适的初始值。例如,在创建一个银行账户对象时,可以使用构造函数设置账户的初始余额。

4.2 资源管理

构造函数可以用于资源的分配和管理。例如,在创建一个文件对象时,可以在构造函数中打开文件,在析构函数中关闭文件,确保资源的正确使用和释放。

4.3 对象的创建和初始化的封装

构造函数可以将对象的创建和初始化过程封装起来,使得类的使用者只需要关注对象的使用,而不需要关心对象的具体初始化细节。

五、构造函数的初始化列表

5.1 语法和作用

初始化列表是在构造函数的参数列表之后、函数体之前使用冒号分隔的一系列初始化语句。它用于在对象的数据成员分配内存后立即对其进行初始化。例如:

#include <iostream>
using namespace std;class Circle {
private:double radius;double area;
public:Circle(double r) : radius(r), area(3.14 * r * r) {}double getArea() {return area;}
};int main() {Circle c(5);cout << "Area: " << c.getArea() << endl;return 0;
}

5.2 初始化列表的优势

  • 效率更高:对于一些类型(如const成员、引用成员),必须使用初始化列表进行初始化。而且,使用初始化列表可以避免对象先进行默认初始化再进行赋值操作,提高了初始化的效率。
  • 避免潜在的问题:在某些情况下,使用初始化列表可以避免一些由于成员初始化顺序不一致而导致的问题。

六、委托构造函数(C++11 及以后)

6.1 定义和作用

委托构造函数是 C++11 引入的一种机制,允许一个构造函数调用同一个类的其他构造函数,从而实现代码的复用。例如: 

#include <iostream>
using namespace std;class MyClass {
private:int a;int b;
public:MyClass(int x, int y) : a(x), b(y) {}MyClass(int x) : MyClass(x, 0) {} // 委托构造函数void print() {cout << "a: " << a << ", b: " << b << endl;}
};int main() {MyClass obj1(1, 2);obj1.print();MyClass obj2(3);obj2.print();return 0;
}

6.2 委托构造函数的优点

  • 代码复用:避免了构造函数中代码的重复,提高了代码的可维护性。
  • 逻辑清晰:将对象的初始化逻辑集中在一个或几个构造函数中,使得代码的逻辑更加清晰。 

七、构造函数的注意事项

7.1 构造函数的重载

类可以有多个构造函数,它们通过参数列表的不同来区分,这就是构造函数的重载。在创建对象时,编译器会根据传递的参数类型和数量来选择合适的构造函数。

7.2 构造函数的异常处理

在构造函数中可能会发生异常,例如动态内存分配失败等。在处理构造函数中的异常时,需要确保对象处于一个合理的状态,避免资源泄漏。

7.3 构造函数与析构函数的配合

构造函数负责对象的创建和初始化,而析构函数负责对象的销毁和资源的释放。它们是相互配合的,确保对象的生命周期管理正确。

八、总结

构造函数是 C++ 面向对象编程中不可或缺的一部分,它为对象的创建和初始化提供了强大而灵活的机制。通过不同类型的构造函数(默认构造函数、带参数的构造函数、拷贝构造函数、移动构造函数等),可以满足各种不同的初始化需求。初始化列表和委托构造函数进一步提高了构造函数的效率和代码的可维护性。同时,在使用构造函数时,需要注意构造函数的重载、异常处理以及与析构函数的配合等问题。深入理解和掌握构造函数的使用,对于编写高质量的 C++ 代码至关重要。

九、参考资料

  •  《C++ Primer(第 5 版)》这本书是 C++ 领域的经典之作,对 C++ 的基础语法和高级特性都有深入讲解。
  • 《Effective C++(第 3 版)》书中包含了很多 C++ 编程的实用建议和最佳实践。
  • 《C++ Templates: The Complete Guide(第 2 版)》该书聚焦于 C++ 模板编程,而using声明在模板编程中有着重要应用,如定义模板类型别名等。
  • C++ 官方标准文档:C++ 标准文档是最权威的参考资料,可以查阅最新的 C++ 标准(如 C++11、C++14、C++17、C++20 等)文档。例如,ISO/IEC 14882:2020 是 C++20 标准的文档,可从相关渠道获取其详细内容。
  • :这是一个非常全面的 C++ 在线参考网站,提供了详细的 C++ 语言和标准库文档。
  • :该网站提供了系统的 C++ 教程,配有丰富的示例代码和清晰的解释,适合初学者学习和理解相关知识。
  • 《C++标准库(第2版)》Nicolai M. Josuttis 著

  • Effective STL Scott Meyers 著

  • C++ Core Guidelines:C++ Core Guidelines

  • C++ Reference:https://en.cppreference.com/w/


相关文章:

  • react组件之间如何使用接收到的className(封装一个按钮案例)
  • MongoDB 集合名称映射问题
  • MongoDB索引
  • 【算法】BFS-解决FloodFill问题
  • React项目添加react-quill富文本编辑器,遇到的问题,比如hr标签丢失
  • Apache SeaTunnel:新一代开源、高性能数据集成工具
  • QTextDocument 入门
  • 屏幕适配常见BUG与兼容性问题
  • 7N60-ASEMI无人机专用功率器件7N60
  • 低空经济 WebGIS 无人机配送 | 图扑数字孪生
  • Linux嵌入式系统SQlite3数据库学习笔记
  • 【数据可视化-22】脱发因素探索的可视化分析
  • 基于 WebRTC + Pion + HeyGem.ai 实现可互动的数字人系统
  • 复刻低成本机械臂 SO-ARM100 3D 打印篇
  • LeetCode 1292 元素和小于等于阈值的正方形的最大边长
  • H5付费进群源码 带分销【源码+教程】虚拟货物系统
  • 作为一个创业团队,Neo4j、Nebula Graph、HugeGraph‌、AllegroGraph‌等几款图数据库哪款更合适?
  • 爆改 toxml 组件 支持数据双向绑定 解决数据刷新问题
  • mybatis mapper.xml中使用枚举
  • AVX2与onnx量化加速
  • 王励勤当选中国乒乓球协会新一任主席
  • 中国英国商会政府事务主席陶克瑞:重庆经济成就瞩目,中英合作机遇无限
  • 中国戏剧奖梅花奖终评启动在即,17场演出公益票将发售
  • 不降息就走人?特朗普试图开先例罢免美联储主席,有无胜算
  • 同比增长4.2%!一季度全国财政支出持续增长
  • 智能网联汽车不得夸大宣传,专家呼吁引导企业规范宣传