C/C++ 基础笔记(十一)类的进阶

本篇 核心知识:构造函数、析构函数、拷贝构造、浅拷贝与深拷贝、断言、静态成员、单例模式


一、构造函数(Constructor)

概念

构造函数是类的特殊成员函数 ,创建对象时自动调用 ,用于初始化对象成员

特性

  1. 语法规则:

    • 函数名与类名完全相同

    • 无返回值(连 void 都没有),不能返回值

    • 创建对象时自动调用 ,无需手动调用,一定会调用构造函数。当类中没有显式定义构造函数,会有默认构造函数

      复制代码
      类名(形参表){ //参数表可以自定义参数类型、个数,可以重载,通过实参确定调用的函数
          函数体;
      }
  2. 默认构造函数:

    • 编译器自动生成,无参数、空函数体

      样式:类名(){}

    • 自定义构造后(有显式定义构造函数),默认构造自动消失

  3. 构造函数重载:

    • 支持多个构造,参数个数 / 类型不同

    • 包括:无参构造、有参构造、拷贝构造

  4. 初始化顺序成员定义顺序决定初始化顺序,与初始化列表顺序无关。

代码示例

复制代码
#include <iostream>
using namespace std;
​
class Person {
    char name[20];
    int age;
public:
    // 无参构造
    Person() {
        strcpy(name, "未知");
        age = 0;
    }
    // 有参构造
    Person(const char* n, int a) {
        strcpy(name, n);
        age = a;
    }
};
​
int main() {
    Person p1;               // 调用无参构造
    Person p2("XXX", 20);   // 调用有参构造
    // Person p3 = {"XXX", 18}; //这种写法只能用在所有数据成员都是公有的
    return 0;
}

拓展:初始化列表

  • 构造函数可通过初始化列表初始化成员,效率更高,尤其适合 const / 引用成员。

    Person(const char* n, int a) : age(a) {
    // age(a) 前面是成员,括号内是参数,如果参数名相同可以不用this区分
    strcpy(name, n);
    }


二、析构函数(Destructor)

概念

析构函数是类的特殊成员函数 ,对象生命周期结束时自动调用 ,用于释放资源、清理内存

特性

  1. 语法规则:

    • 函数名:~ 类名(~是函数名的一部分)。

    • 无返回类型和返回值、形参表必须是空的(不能重载)。

  2. 调用时机:

    • 栈区对象:离开作用域自动调用

    • 堆区对象(new创建):delete时调用。

    • 生命周期结束时自动调析构。(因果关系不是调用了析构就结束生命周期)

  3. 默认析构 :如果没有显式析构则编译器自动生成,空函数体

  4. 执行顺序先构造、后析构先创建后析构、后创建先析构

代码示例

复制代码
class Person {
public:
    Person() { cout << "构造" << endl; }
    ~Person() { cout << "析构" << endl; }
};
​
int main() {
    Person p; // 栈区创建
    Person *p1 = new Person(); // 堆区创建
    delete p1; // 堆区析构
    return 0; // 栈区析构
}

拓展:析构与生命周期

  • 析构是生命周期结束的善后操作,不是结束原因。

  • 手动调用析构不结束对象,仅执行清理逻辑。


三、拷贝构造函数(Copy Constructor)

概念

拷贝构造是特殊构造函数 ,用已有对象初始化新对象

特殊在首个显式定义的参数是当前类的引用类型

特性

  1. 语法类名(const 类名& 别名)必须是 const 引用参数保护实参

  2. 调用场景:

    • (1)用一个现有的对象初始化新对象 (隐式)

    • (2)用一个现有的对象创建另一个对象 (显式)

    • 函数参数为当前类的对象(值传递)。

    • 函数返回值为当前类的对象(拷贝值返回,return后出函数有析构函数会直接析构,流程:构造-拷贝-析构-析构)。

  3. 默认拷贝 :编译器自动生成,逐字节浅拷贝

代码示例

复制代码
class Person {
    int age;
public:
    Person(int a) : age(a) {}
    // 拷贝构造(参数是当前类的引用类型)
    Person(const Person &ref) { // 必须用引用 给形参分配临时内容,把实参拷贝给形参
    // 也可以用初始化列表,当有成员必须初始化时(const int age)必须写初始化列表 :age(ref.age)
        age = ref.age;
        cout << "拷贝构造" << endl;
    }
};
​
int main() {
    Person p1(20);
    Person p2 = p1; // 调用隐式拷贝构造
    Person p3(p1);  // 调用显式拷贝构造
    
    // 注意
    Person p4(0); //构造
    Person p5;    //构造
    p5 = p4; // 这里是赋值函数,不是拷贝构造
}

相似概念:值传递 vs 引用传递

  • 拷贝构造必须用引用 ,否则会无限递归(传值→拷贝→传值...)。

四、浅拷贝与深拷贝

概念

  • 浅拷贝 :默认拷贝,一一给成员值,指针共享同一块内存

  • 深拷贝 :手动实现,指针重新申请内存、复制数据,各自独立。

特性

  1. 浅拷贝问题:

    • 多个对象共享同一块堆内存

    • 析构时重复释放内存,触发断言错误。

  2. 深拷贝实现:

    • 拷贝构造中为指针重新申请内存

    • 逐元素复制原数据

代码示例

复制代码
class Array {
public:
    int* data;
    int size;
public:
    // 构造函数
    Array(int size = 0) {
        this->size = size;
        data = new int[s]; // 申请内存
    }
    // 浅拷贝 一一对应给值
    Array(const Array& other) {
        this->size = other.size;
        this->data = other.data; // 指针不能直接拷贝指向,会共用内存
    }
    // 深拷贝 先申请新的内存 再把原内存中的数据拷贝到新内存
    Array(const Array& other) {
        this->size = other.size;
        data = new int[size]; // 新内存
        // 拷贝方法一
        memcpy(this->data, other.data, sizeof(int)*size);
        // 拷贝方法二
        for(int i=0; i<size; i++)
            data[i] = other.data[i]; // 在新内存中循环赋值
    }
    ~Array() {
        delete[] data; // 释放指针内存
        data = nullptr;
    }
};

拓展:何时用深拷贝

  • 类包含指针 / 动态内存成员 时,必须手动深拷贝

五、断言(assert)

概念

断言是调试宏 ,用于检查条件是否为真,假则终止程序并报错。

特性

  1. 头文件<assert.h>

  2. 语法assert(条件)

  3. 作用 :调试阶段校验逻辑合法性(如指针非空、下标合法)。

  4. 发布模式 :定义NDEBUG可关闭断言。

代码示例

复制代码
#include <assert.h>
int main() {
    int a = 5;
    assert(a > 0); // 条件真,继续
    assert(a < 0); // 条件假,终止程序,报错
    return 0;
}

拓展:断言 vs if

  • 断言:调试用 ,发布可关闭,用于内部逻辑校验

  • if:正式逻辑 ,不可关闭,用于正常业务判断


六、静态成员(static)

概念

静态成员属于整个类所有对象共享 ,生命周期为程序全程

特性

1. 静态成员变量
  • 定义:static 类型 变量名;(类内声明)。

  • 初始化:必须在类外全局区初始化类型 类名::变量名 = 值;)。

  • 访问:类名::变量名 或 对象访问。

  • 特点:所有对象共享同一份数据

2. 静态成员函数
  • 定义:static 返回值 函数名(参数);

  • 访问:类名::函数名 ()

  • 特点:无 this 指针访问不了普通数据成员,只能访问静态成员

代码示例

复制代码
class Student {
public:
    static int count; // 静态变量
    static void show() { // 静态函数
        cout << "学生数:" << count << endl;
    }
};
// 类外初始化
int Student::count = 0;
​
int main() {
    Student::count = 10;
    Student::show();
    return 0;
}

拓展:静态成员用途

  • 统计对象个数、全局配置、工具函数。

七、单例模式(Singleton)

概念

单例模式确保类只有一个实例,全局共享,常用于管理类、工具类。

特性

  1. 核心步骤:

    • 私有构造:限制构造的使用,提供一个静态函数禁止外部创建对象。

    • 静态实例:类内静态指针 / 对象。

    • 静态获取函数:返回唯一实例。

  2. 两种实现:

    • 饿汉模式:程序启动即创建实例。(不管是否需要,先创建并且初始化)

      • 优点:简单、线程安全、没有并发问题

      • 缺点:程序启动就占内存(如果一直不用就浪费)

    • 懒汉模式:首次调用时创建实例。(等到需要使用的时候再创建)

      • 优点:节省内存、用到才创建

      • 缺点:多线程不安全,必须加锁(C++11 之后有最简单写法)

代码示例

复制代码
// 饿汉模式
class Singleton {
private:
    static Singleton instance; // 声明静态成员变量:属于整个类,唯一的单例对象
    Singleton() {} // 构造函数私有化,禁止外部直接创建对象(保证唯一)
public:
    static Singleton& getInstance() { // 公共静态接口:给外部获取唯一实例的入口
        return instance; // 返回已经创建好的唯一实例
    }
};
Singleton Singleton::instance; // 类外全局初始化(程序启动就创建)

// 懒汉模式
class LazySingleton {
private:
    static LazySingleton* instance; // 静态指针:保存唯一实例的地址。初始为 nullptr,还没创建对象
    LazySingleton() {} // 构造函数私有化 禁止外部 new 对象,保证只能内部创建
public:
    static LazySingleton* getInstance() { // 获取唯一实例的静态接口
        if(!instance) // 判断:如果还没创建实例
        	instance = new LazySingleton(); // 第一次调用才创建
        return instance;
    }
};
LazySingleton* LazySingleton::instance = nullptr; // 类外初始化静态指针,一开始 = nullptr,还没有创建对象

拓展:单例注意

私有或禁用拷贝构造和赋值,防止拷贝生成新实例。

相关推荐
-森屿安年-2 小时前
1137. 第 N 个泰波那契数
c++·动态规划
程序员老舅3 小时前
从内核视角,看Linux文件读写过程
linux·服务器·c++·内核·linux内核·vfs·linux内存
Soari3 小时前
llama.cpp更新(b9553):LLM inference in C/C++,本地和云端实现高性能大模型推理
c语言·c++·llama
2601_961194023 小时前
考研资料电子版|去哪找|网盘
java·c语言·c++·python·考研·php
Peter·Pan爱编程3 小时前
23. 算法库:用算法代替手写循环
c++·人工智能·算法
大白话_NOI4 小时前
【洛谷 P1303】A*B Problem + 详细分析
c++
十月的皮皮4 小时前
C语言学习笔记202606008- 三角形判断(3种方法)
c语言·笔记·学习
小欣加油4 小时前
leetcode2161 根据给定数字划分数组
数据结构·c++·算法·leetcode·职场和发展
吃着火锅x唱着歌4 小时前
深度探索C++对象模型 学习笔记 第五章 构造、解构、拷贝语意学(2)
c++·笔记·学习