【C++】构造函数

构造函数概述

基本概念

  • 定义:构造函数是与类同名的特殊成员函数
  • 返回值:没有返回值(包括void类型)
  • 调用时机:在创建对象时自动调用
  • 主要作用:对象初始化工作,如数据成员赋值、资源分配、设置初始状态等

核心特性

  • 自动调用:创建对象时由编译器自动调用,不能手动显式调用
  • 重载支持:可以定义多个构造函数,支持不同的初始化方式
  • 无返回类型:连void都不需要写

构造函数的底层机制

编译器隐式调用:构造函数在对象创建时由编译器自动插入调用代码,程序员不能直接调用。

对象内存布局:在对象内存分配完成后(栈或堆),构造函数被调用,负责初始化对象的数据成员。

初始化与赋值的区别

  • 初始化:在对象创建时直接赋予初始值(如使用初始化列表)。
  • 赋值:先创建对象(可能未初始化),再赋予值。
  • 初始化通常效率更高,尤其是对于类类型成员。

构造函数分类

默认构造函数

特点

  • 不带任何参数
  • 如果没有定义任何构造函数,编译器会自动生成一个空的默认构造函数
  • 如果定义了其他构造函数,编译器不再自动生成默认构造函数

示例:

cpp 复制代码
class Point {
public:
    // 默认构造函数
    Point() {
        x = 0;
        y = 0;
    }
    
    // 成员变量
    int x, y;
};

// 使用示例
Point p1;        // 调用默认构造函数
Point p2{};      // C++11 统一初始化

编译器生成的默认构造函数

  • 如果类中没有定义任何构造函数,编译器会隐式生成一个合成默认构造函数
  • 合成默认构造函数会:
    • 调用类类型成员的默认构造函数。
    • 对于内置类型成员,不会进行初始化(值是未定义的)。

= default= delete

  • = default 显式要求编译器生成默认构造函数。
  • = delete 禁止编译器生成默认构造函数。

带参数的构造函数

特点

  • 接受参数用于初始化对象属性
  • 支持多种初始化方式

示例:

cpp 复制代码
class Point {
public:
    // 带参数的构造函数
    Point(int xVal, int yVal) {
        x = xVal;
        y = yVal;
    }
    
    // 重载的构造函数
    Point(int val) {
        x = val;
        y = val;
    }
    
    int x, y;
};

// 使用示例
Point p1(10, 20);    // 调用两个参数的构造函数
Point p2(5);         // 调用一个参数的构造函数

函数重载解析

  • 编译器根据传递的参数数量和类型选择最匹配的构造函数。
  • 如果存在多个匹配,可能产生二义性错误

复制构造函数

作用

  • 用已存在的对象创建新对象
  • 实现对象的深拷贝或浅拷贝
  • 在以下情况自动调用:
    • 对象作为函数参数传递(值传递)
    • 对象作为函数返回值(值返回)
    • 用已有对象初始化新对象

示例:

cpp 复制代码
class Person {
private:
    int age;
    int height;
    int weight;

public:
    // 带参数的构造函数
    Person(int a, int h, int w) : age(a), height(h), weight(w) {}
    
    // 复制构造函数
    Person(const Person& other) {
        age = other.age;
        height = other.height;
        weight = other.weight;
    }
};

// 使用示例
int main() {
    Person p1(20, 30, 100);  // 调用带参构造函数
    Person p2(p1);           // 调用复制构造函数
    Person p3 = p1;          // 调用复制构造函数
    return 0;
}

合成复制构造函数

  • 如果未定义复制构造函数,编译器会生成一个。
  • 合成复制构造函数执行浅拷贝(按成员复制)。

深拷贝与浅拷贝

  • 浅拷贝:只复制指针值,不复制指向的资源。
  • 深拷贝:复制指针指向的整个资源。
  • 当类包含动态分配的资源时,必须自定义复制构造函数实现深拷贝。

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

背景

  • 为支持移动语义,C++11引入了移动构造函数。

作用

  • 用于从临时对象(右值)"窃取"资源,避免不必要的深拷贝。

示例

cpp 复制代码
class MyVector {
    int* data;
    size_t size;
public:
    // 移动构造函数
    MyVector(MyVector&& other) noexcept 
        : data(other.data), size(other.size) {
        other.data = nullptr;
        other.size = 0;
    }
};

构造函数的进阶特性

初始化列表

优势

  • 效率更高(直接初始化而非先定义后赋值)
  • 必须用于常量成员和引用成员的初始化
  • 初始化顺序与声明顺序一致

示例

cpp 复制代码
class Student {
private:
    const int id;        // 常量成员
    string& nameRef;     // 引用成员
    int age;
    
public:
    // 使用初始化列表的构造函数
    Student(int studentId, string& name, int studentAge) 
        : id(studentId), nameRef(name), age(studentAge) {
        // 构造函数体
    }
};

成员初始化顺序

  • 初始化顺序由类中成员的声明顺序决定,与初始化列表中的顺序无关。
  • 不遵循声明顺序可能导致依赖错误。

效率优势

  • 初始化列表直接在成员变量创建时赋值,避免先调用默认构造函数再赋值的开销。

委托构造函数

作用:一个构造函数可以调用同一个类的另一个构造函数

示例

cpp 复制代码
class Rectangle {
private:
    int width, height;
    
public:
    // 委托构造函数
    Rectangle() : Rectangle(1, 1) {}  // 委托给两个参数的构造函数
    
    Rectangle(int size) : Rectangle(size, size) {}  // 委托给两个参数的构造函数
    
    Rectangle(int w, int h) : width(w), height(h) {}  // 实际工作的构造函数
};

委托链

  • 委托构造函数可以形成一个链式调用,但不能形成环
  • 最终必须有一个构造函数完成所有成员的初始化。

explicit 关键字

作用:防止构造函数的隐式转换

cpp 复制代码
class MyClass {
private:
    int value;
    
public:
    explicit MyClass(int v) : value(v) {}  // 禁止隐式转换
};

// 使用示例
MyClass obj1(10);      // 正确:显式调用
// MyClass obj2 = 10;  // 错误:explicit 禁止隐式转换

隐式转换的场景

  • 单参数构造函数允许编译器进行隐式类型转换。
  • explicit 阻止这种转换,要求必须显式调用构造函数。

特殊注意事项

构造函数与内存管理

cpp 复制代码
class DynamicArray {
private:
    int* data;
    int size;
    
public:
    // 构造函数 - 分配资源
    DynamicArray(int sz) : size(sz) {
        data = new int[size];  // 动态内存分配
    }
    
    // 析构函数 - 释放资源
    ~DynamicArray() {
        delete[] data;         // 释放动态内存
    }
};

RAII原则

  • 构造函数用于获取资源,析构函数用于释放资源。
  • 确保资源在对象生命周期内有效,避免资源泄漏。

默认构造函数的必要性

cpp 复制代码
class Container {
private:
    vector<int> items;
    
public:
    // 如果注释掉默认构造函数,下面的代码会编译错误
    Container() = default;  // 显式要求编译器生成默认构造函数
    
    // 其他构造函数
    Container(const vector<int>& initialItems) : items(initialItems) {}
};

// 使用示例
Container containers[10];  // 需要默认构造函数

STL容器与默认构造函数

  • 许多STL容器(如vector)要求元素类型必须有默认构造函数,以便在扩容时构造新元素。

聚合类初始化

  • 如果类满足聚合类的条件,可以使用列表初始化而不需要默认构造函数。
相关推荐
独自破碎E6 小时前
【曼哈顿距离】BISHI25 最大 FST 距离
java·开发语言
苏涵.6 小时前
Java三大集合:List、Set、Map
java·开发语言
Amumu121386 小时前
Vue3 Composition API(一)
开发语言·javascript·ecmascript
存在的五月雨6 小时前
Spring Security认证流程
java·开发语言·mysql
树码小子6 小时前
综合练习:验证码案例(1)总体设计
java·开发语言·spring
草莓熊Lotso6 小时前
Qt 主窗口核心组件实战:菜单栏、工具栏、状态栏、浮动窗口全攻略
运维·开发语言·人工智能·python·qt·ui
Ronin3056 小时前
持久化数据管理中心模块
开发语言·c++·rabbitmq·gtest
froginwe116 小时前
AJAX 实例详解
开发语言
魔力军6 小时前
Rust学习Day2: 变量与可变性、数据类型和函数和控制流
开发语言·学习·rust