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

深入理解构造函数:C++ 编程中的基石

一、概念

        构造函数(Constructor) 是一种特殊的成员函数,用于在创建对象时初始化对象的状态(即成员变量)。它的主要作用是保证对象在创建时具有有效的初始值。


二、特点

与类同名

  • 构造函数的名称与类名相同,没有返回值(甚至不能写 void)。

自动调用

  • 构造函数在对象创建时自动调用,无需显式调用。

可以重载

  • 构造函数可以有多个版本,依赖于参数个数和类型(重载)。

无返回值

  • 构造函数没有返回值,也不能通过返回值来传递信息。

默认构造函数

  • 如果用户没有定义构造函数,编译器会自动生成一个无参的默认构造函数。
  • 如果用户定义了任何构造函数,编译器将不再生成默认构造函数。

三、种类

1. 默认构造函数

无参数的构造函数,称为默认构造函数。

  • 作用:初始化对象为默认状态。
  • 示例:
class Example {
public:
    Example() {  // 默认构造函数
        cout << "Default constructor called" << endl;
    }
};

int main() {
    Example obj;  // 自动调用默认构造函数
    return 0;
}

2. 带参数的构造函数

构造函数可以带参数,用于初始化对象时传递参数。

  • 示例:
class Example {
private:
    int value;
public:
    Example(int v) {  // 带参数的构造函数
        value = v;
        cout << "Constructor called with value: " << value << endl;
    }
};

int main() {
    Example obj(10);  // 调用带参数的构造函数
    return 0;
}

3. 拷贝构造函数

拷贝构造函数用于创建一个对象时,以另一个同类型的对象对其初始化。

  • 特点:参数是同类的引用类型,通常形式为 ClassName(const ClassName &obj)
  • 示例:
class Example {
private:
    int value;
public:
    Example(int v) {
        value = v;
        cout << "Parameterized constructor called" << endl;
    }
    Example(const Example &obj) {  // 拷贝构造函数
        value = obj.value;
        cout << "Copy constructor called" << endl;
    }
};

int main() {
    Example obj1(10);         // 调用带参数的构造函数
    Example obj2 = obj1;      // 调用拷贝构造函数
    return 0;
}

4. 委托构造函数

一个构造函数可以调用另一个构造函数来简化代码逻辑。

  • 示例:
class Example {
private:
    int value;
public:
    Example() : Example(0) {  // 调用另一个构造函数
        cout << "Default constructor called" << endl;
    }
    Example(int v) {
        value = v;
        cout << "Parameterized constructor called with value: " << value << endl;
    }
};

int main() {
    Example obj;   // 调用默认构造函数,同时委托到带参数的构造函数
    return 0;
}

5. 默认和删除的构造函数

  • 可以显式指定构造函数为默认的或删除的(C++11 引入)。
  • 示例:
class Example {
public:
    Example() = default;  // 默认构造函数
    Example(int) = delete;  // 禁止使用此构造函数
};

int main() {
    Example obj1;     // 可以
    // Example obj2(10);  // 错误,无法使用删除的构造函数
    return 0;
}

四、构造函数的使用与初始化列表

初始化成员变量

可以在构造函数中直接初始化成员变量,或使用 初始化列表 初始化。

  • 示例:
class Example {
private:
    int a;
    int b;
public:
    Example(int x, int y) : a(x), b(y) {  // 初始化列表
        cout << "a = " << a << ", b = " << b << endl;
    }
};

int main() {
    Example obj(10, 20);  // 调用构造函数
    return 0;
}

初始化列表

  • 初始化列表在对象构造时直接赋值,而不是先默认构造后再赋值,因此效率更高。
  • 某些情况必须使用初始化列表,例如常量成员变量或引用类型。

五、子类构造函数

工作流程

  1. 创建子类对象时,必须先调用父类的构造函数。
  2. 如果子类的构造函数没有显式调用父类的构造函数,则会默认调用父类的无参构造函数。
  3. 如果父类没有无参构造函数,子类必须显式调用父类的某个构造函数。

示例:

#include <iostream>
using namespace std;

class Parent {
public:
    Parent(int x) {
        cout << "Parent constructor called with value: " << x << endl;
    }
};

class Child : public Parent {
public:
    Child(int x) : Parent(x) {  // 显式调用父类的构造函数
        cout << "Child constructor called" << endl;
    }
};

int main() {
    Child obj(10);  // 调用子类构造函数
    return 0;
}

输出:

Parent constructor called with value: 10
Child constructor called

六、构造函数的注意点

没有返回值: 构造函数没有返回值,不能使用 return 返回值。

拷贝构造函数的深拷贝与浅拷贝: 如果类中有指针类型成员变量,拷贝构造函数默认执行浅拷贝,这可能会导致资源冲突,建议实现深拷贝。

静态成员的初始化: 静态成员变量需要在类外初始化,不能通过构造函数初始化。

继承中的构造函数:父类的构造函数不会被子类继承,但可以通过子类构造函数显式调用。


构造函数与析构函数的关系

  • 析构函数与构造函数相反,用于对象销毁时释放资源。
  • 析构函数在子类对象销毁时按照从子类到父类的顺序调用。

示例:

#include <iostream>
using namespace std;

class Parent {
public:
    Parent() { cout << "Parent constructor called" << endl; }
    ~Parent() { cout << "Parent destructor called" << endl; }
};

class Child : public Parent {
public:
    Child() { cout << "Child constructor called" << endl; }
    ~Child() { cout << "Child destructor called" << endl; }
};

int main() {
    Child obj;  // 创建子类对象
    return 0;
}

输出:

Parent constructor called
Child constructor called
Child destructor called
Parent destructor called

七、总结

  1. 构造函数是初始化对象的入口,可以重载。
  2. 带参数构造函数和拷贝构造函数提供了灵活的初始化方式。
  3. 初始化列表是高效的初始化方式,尤其适合常量或引用类型。
  4. 父类的构造函数必须在子类构造函数中显式调用(如果没有无参构造函数)。
  5. 析构函数与构造函数互补,用于释放资源。

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

相关文章:

  • 下载运行Vue开源项目vue-pure-admin
  • 如何解决 ‘adb‘ 不是内部或外部命令,也不是可运行的程序或批处理文件的问题
  • MyBatis 中常用标签
  • 玩转OCR | 探索腾讯云智能结构化识别新境界
  • LLMs之PDF:MinerU(将PDF文件转换成Markdown和JSON格式)的简介、安装和使用方法、案例应用之详细攻略
  • GraalVM完全指南:云原生时代下使用GraalVM将Spring Boot 3应用转换为高效Linux可执行文件
  • C#中var关键字
  • 【MySQL篇】聚合查询,联合查询
  • 南开大学师唯教授团队:配位聚合物和金属-有机框架在电池中的应用
  • 从零开始C++棋牌游戏开发之第二篇:初识 C++ 游戏开发的基本架构
  • 基础8:可调用对象类型
  • Trim_Galore_User_Guide
  • 【AI日记】24.12.23 kaggle 比赛 2-11
  • Slate文档编辑器-TS类型扩展与节点类型检查
  • 详细解读sedex验厂
  • 【微信小程序】1|底部图标 | 我的咖啡店-综合实训
  • 搭建Elastic search群集
  • 利用Matlab绘制心性函数
  • ChatGPT生成接口文档实践案例(二)
  • ubuntu开机进入initramfs状态
  • Java基础面试题20:Java语言sendRedirect()和forward()方法有什么区别?
  • linux普通用户使用sudo不需要输密码
  • [创业之路-206]:《华为战略管理法-DSTE实战体系》- 6-关键成功因素法CSF
  • FreeMarker语法
  • 【潜意识Java】深度解析黑马项目《苍穹外卖》与蓝桥杯算法的结合问题
  • Linux下载时出现的错误(配置阿里云镜像解决)