C++ 类核心知识总结
本文是个人学习笔记,理解可能有误,欢迎指正或补充!
目录
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. 总结
- 构造函数:与类同名、无返回值、支持重载,创建对象时自动调用,核心作用是初始化对象;初始化列表是更高效的初始化方式,推荐优先使用。
- 析构函数:格式为
~类名()、无参数、不能重载,对象销毁时自动调用;若对象占用动态资源(如new内存),必须手动实现析构函数释放资源,避免内存泄漏。 - 类的核心特性:封装性通过
private/public/protected控制数据访问,this指针指向当前对象,静态成员属于类本身,const成员函数保证只读操作。