C++ 类核心知识总结

13 阅读9分钟

C++ 类核心知识总结

本文是个人学习笔记,理解可能有误,欢迎指正或补充!

目录

  1. 类的基本结构与访问控制
  2. 构造函数(Constructor)
  3. 析构函数(Destructor)
  4. 构造函数的进阶用法
  5. 对象的生命周期与构造/析构执行顺序
  6. 类的其他核心要点
  7. 总结

1. 类的基本结构与访问控制

类是 C++ 面向对象编程的核心,用于封装数据(成员变量)和操作数据的行为(成员函数)。类的定义以class关键字开头,主体包含在大括号{}中,末尾必须加分号;

1.1 基础结构代码示例

#include <iostream>
#include <string>
using namespace std;

class Person {
    // 1. 私有成员(默认权限):仅类内部可访问,外部不可直接操作
private:
    string name;  // 私有成员变量:封装核心数据,避免外部直接修改
    int age;

    // 2. 保护成员:类内部 + 子类可访问,外部不可访问
protected:
    string gender;

    // 3. 公有成员:类内部 + 外部可访问,是类对外的接口
public:
    // 构造函数、析构函数、普通成员函数的声明(必须放在public中)
    Person();                  // 无参构造函数
    Person(string n, int a);   // 有参构造函数(重载)
    ~Person();                 // 析构函数

    void setInfo(string n, int a); // 成员函数:操作私有数据的接口
    void showInfo();             // 成员函数:展示对象数据
};

1.2 访问权限核心规则

权限关键字访问范围核心用途
private仅类内部封装核心数据,杜绝外部直接修改
public类内部 + 外部(对象/指针)对外提供操作接口(构造/成员函数)
protected类内部 + 子类为继承场景预留访问权限

2. 构造函数(Constructor)

构造函数是类的特殊成员函数,核心作用是初始化对象,在创建对象时自动调用,无法手动调用。

2.1 核心特性

  • 命名规则:函数名与类名完全一致,无返回值(连void都不能写);
  • 自动调用:创建对象时(如Person p;/Person p("张三", 20);)立即执行;
  • 支持重载:可定义多个构造函数,通过参数列表(个数、类型、顺序)区分;
  • 默认构造:若未手动定义任何构造函数,编译器会自动生成无参默认构造(空实现);若手动定义了构造函数,编译器不再生成默认构造。

2.2 代码实现(类内声明 + 类外定义)

// 1. 无参构造函数(初始化默认值)
Person::Person() {
    name = "未知";
    age = 0;
    gender = "未知";
    cout << "无参构造函数被调用,对象初始化完成" << endl;
}

// 2. 有参构造函数(自定义初始化值)
Person::Person(string n, int a) {
    name = n;
    age = a;
    gender = "未知";
    cout << "有参构造函数被调用,对象初始化完成" << endl;
}

// 3. 成员函数:操作私有数据的接口
void Person::setInfo(string n, int a) {
    name = n;
    age = a;
}

void Person::showInfo() {
    cout << "姓名:" << name << ",年龄:" << age << endl;
}

3. 析构函数(Destructor)

析构函数是类的特殊成员函数,核心作用是清理对象占用的资源(如动态内存、打开的文件、网络连接等),在对象生命周期结束时自动调用。

3.1 核心特性

  • 命名规则~类名(),无返回值、无参数,不能重载(一个类只能有一个析构函数);
  • 自动调用:对象销毁时执行(局部对象出作用域、动态对象被delete、程序结束);
  • 默认析构:若未手动定义,编译器生成空实现的默认析构;若对象占用动态资源(new分配的内存),必须手动编写析构函数释放资源,避免内存泄漏。

3.2 代码实现(含资源释放示例)

// 1. 基础析构函数
Person::~Person() {
    cout << "析构函数被调用,对象 " << name << " 已销毁" << endl;
}

// 2. 带动态资源的析构示例(重点:避免内存泄漏)
class Student {
private:
    string* major; // 动态分配的字符串(堆区内存)
public:
    // 构造函数:分配堆区内存
    Student(string m) {
        major = new string(m); // 堆区分配内存
        cout << "Student 构造,分配专业内存" << endl;
    }

    // 析构函数:必须释放堆区内存
    ~Student() {
        delete major; // 释放动态内存,避免内存泄漏
        cout << "Student 析构,释放专业内存" << endl;
    }

    void showMajor() {
        cout << "专业:" << *major << endl;
    }
};

4. 构造函数的进阶用法

4.1 拷贝构造函数

用于用一个已存在的对象初始化新对象,是构造函数的一种重载形式,格式为:类名(const 类名& 源对象)

4.1.1 代码实现
// 拷贝构造函数声明(类内)
class Person {
    // 省略其他成员...
public:
    Person(const Person& p); // 拷贝构造函数声明
};

// 拷贝构造函数定义(类外)
Person::Person(const Person& p) {
    name = p.name;
    age = p.age;
    gender = p.gender;
    cout << "拷贝构造函数被调用" << endl;
}

// 拷贝构造的使用场景
int main() {
    Person p1("李四", 25); // 调用有参构造
    Person p2 = p1;        // 场景1:赋值初始化,调用拷贝构造
    Person p3(p1);         // 场景2:直接初始化,调用拷贝构造
    return 0;
}

4.2 初始化列表(推荐用法)

更高效的对象初始化方式,直接初始化成员变量(而非在构造函数体内赋值),避免“先默认初始化、再赋值”的二次操作,尤其适合常量成员、引用成员的初始化。

4.2.1 代码实现
// 用初始化列表改写有参构造函数
Person::Person(string n, int a) 
    : name(n), age(a), gender("未知") { // 初始化列表:直接给成员变量赋值
    cout << "初始化列表版构造函数被调用" << endl;
}

5. 对象的生命周期与构造/析构执行顺序

对象的生命周期决定了构造函数和析构函数的执行时机,不同类型的对象(局部、动态)执行顺序不同。

5.1 完整代码示例

int main() {
    cout << "===== 局部对象 =====" << endl;
    Person p1;          // 调用无参构造
    Person p2("王五", 30); // 调用有参构造
    p2.showInfo();

    cout << "===== 动态对象 =====" << endl;
    Person* p3 = new Person("赵六", 35); // 调用有参构造
    p3->showInfo();
    delete p3; // 手动释放动态对象,触发析构函数

    cout << "===== 主函数结束 =====" << endl;
    return 0; // 局部对象 p1、p2 出作用域,依次调用析构函数
}

5.2 执行结果顺序

===== 局部对象 =====
无参构造函数被调用,对象初始化完成
有参构造函数被调用,对象初始化完成
姓名:王五,年龄:30
===== 动态对象 =====
有参构造函数被调用,对象初始化完成
姓名:赵六,年龄:35
析构函数被调用,对象 赵六 已销毁
===== 主函数结束 =====
析构函数被调用,对象 王五 已销毁
析构函数被调用,对象 未知 已销毁

5.3 核心规律

  • 局部对象:构造 → 出作用域 → 析构(先创建后销毁);
  • 动态对象:new → 构造 → delete → 析构(手动释放才会析构,否则内存泄漏);
  • 全局对象:程序启动时构造 → 程序结束时析构。

6. 类的其他核心要点

6.1 封装性

通过private隐藏内部数据,仅通过public成员函数对外提供访问接口,保证数据安全性(比如可以在setInfo中校验年龄的合法性)。

6.2 this指针

每个非静态成员函数都隐含一个this指针,指向当前调用函数的对象,可用于区分成员变量和参数:

void Person::setInfo(string name, int age) {
    this->name = name; // this->name 是成员变量,name 是参数
    this->age = age;
}

6.3 静态成员

static修饰的成员变量/函数,属于类本身(而非单个对象),所有对象共享,可通过类名::成员名直接访问:

class Person {
public:
    static int count; // 静态成员变量:统计对象数量
    Person() { count++; } // 构造时计数+1
    static void showCount() { // 静态成员函数:访问静态成员
        cout << "对象总数:" << count << endl;
    }
};
// 静态成员变量必须在类外初始化
int Person::count = 0;

// 使用示例
int main() {
    Person p1, p2;
    Person::showCount(); // 输出:对象总数:2
    return 0;
}

6.4 const成员函数

函数末尾加const,表示该函数不会修改对象的成员变量,仅做只读操作:

class Person {
public:
    void showInfo() const { // const成员函数
        cout << "姓名:" << name << ",年龄:" << age << endl;
        // age = 100; // 错误:const函数不能修改成员变量
    }
};

7. 总结

  1. 构造函数:与类同名、无返回值、支持重载,创建对象时自动调用,核心作用是初始化对象;初始化列表是更高效的初始化方式,推荐优先使用。
  2. 析构函数:格式为~类名()、无参数、不能重载,对象销毁时自动调用;若对象占用动态资源(如new内存),必须手动实现析构函数释放资源,避免内存泄漏。
  3. 类的核心特性:封装性通过private/public/protected控制数据访问,this指针指向当前对象,静态成员属于类本身,const成员函数保证只读操作。