类的特殊成员
const 成员
const 是C语言延续的关键字,核心语义为只读不可修改,在类中用于限定成员(数据/方法)的只读属性,帮助编译器优化,提升代码安全性与效率。
const 类数据(成员属性)
核心用途
修饰一旦初始化就不可修改的类属性(如身份证号、学号、固定名称等)。
初始化方式(两种)
| 初始化方式 | 语法示例 | 特点与适用场景 |
|---|---|---|
| 构造函数初始化列表 | cpp class Person { const unsigned int ID; public: Person(unsigned int id) : ID(id) {} // 初始化列表赋值 }; |
1. 支持对象创建时传入不同初始值(如不同学生的学号) 2. 兼容性强,支持所有C++标准 |
| 类内直接初始化 | cpp class Person { const unsigned int ID = 1234; // 类内定死值 }; |
1. 所有对象共享同一固定值,不可修改 2. 仅支持C++11及以上标准,老旧编译器可能报错 3. 适用场景:类的公共固定属性(如学校名称) |
典型示例
class Student {
private:
// 所有学生共享的固定属性(类内初始化)
const string schoolName = "XXX学院";
// 每个学生独有的只读属性(初始化列表赋值)
const int ID;
public:
// 构造函数初始化列表为ID赋值
Student(int id) : ID(id) {}
};
int main() {
Student Jack(001); // ID=001,schoolName=XXX学院
Student Lucy(002); // ID=002,schoolName=XXX学院
}
注意事项
const类数据不可在构造函数体内部赋值(仅能通过初始化列表或类内初始化);- 类内初始化的
const成员,本质是编译期常量(若值为字面量),内存占用更高效。
const 类方法
核心用途
声明该方法不会修改类的任何数据成员,也不会调用非 const 方法,是“只读操作”的明确标识。
语法规则
const关键字必须放在函数参数列表之后;- 声明与定义中必须同时包含
const(const是方法重载的依据之一)。
示例代码
// 头文件 Person.h
#ifndef _PERSON_H
#define _PERSON_H
#include <string>
using namespace std;
class Person {
int age;
string name;
public:
// 声明时加const
void showInfo() const;
void setName(string newName); // 非const方法
};
#endif
// 实现文件 Person.cpp
#include "Person.h"
// 定义时必须加const,与声明一致
void Person::showInfo() const {
// 错误:const方法不能修改成员数据
// age = 100;
// 错误:const方法不能调用非const方法
// setName("刘德华");
cout << "姓名:" << name << ",年龄:" << age << endl;
}
注意事项
const对象只能调用const类方法(非const对象可调用所有方法);- 逻辑上不修改成员数据的方法,务必声明为
const(提升编译优化效率,减少误操作)。
拓展:const 与 static 的组合
static const 类数据(C++11前常用)
class Student {
// 类内声明(必须类外定义)
static const int MAX_SCORE = 100;
};
// 类外定义(C++11前必须,C++11后可省略,但建议保留兼容性)
const int Student::MAX_SCORE;
- 特性:所有对象共享、只读、编译期常量(值需为字面量);
- 适用场景:类的公共常量(如满分、最大容量)。
constexpr 替代方案(C++11+)
class Student {
// 编译期常量,兼具static和const特性,无需类外定义
constexpr static int MAX_SCORE = 100;
};
- 优势:语法更简洁,支持更多编译期计算场景。
静态成员
被 static 修饰的类成员,属于类本身而非单个对象,用于表达类的公共属性/行为(如总人数、公共工具方法)。
静态成员数据
语法规则
- 类内声明(加
static),类外定义(分配内存,不加static); - 定义时需指定类作用域(
类名::成员名)。
示例代码(学生总人数统计)
#include <iostream>
#include <string>
using namespace std;
class Student {
private:
// 单个对象属性
unsigned int ID;
string name;
// 类的公共属性(声明)
static int total;
public:
// 构造函数:创建对象时总人数+1
Student(unsigned int id, string n) : ID(id), name(n) {
total++;
}
// 静态方法:获取总人数
static void showTotal() {
cout << "学生总人数:" << total << endl;
}
};
// 静态成员数据定义(类外,分配内存,初始值默认0)
int Student::total;
int main() {
Student Jack(001, "Jack");
Student Lucy(002, "Lucy");
// 两种访问方式:类名::方法 或 对象.方法
Student::showTotal(); // 输出:学生总人数:2
Jack.showTotal(); // 输出:学生总人数:2
return 0;
}
核心特性
| 特性 | 说明 |
|---|---|
| 存储位置 | 静态数据区(独立于对象,不占用对象内存) |
| 共享性 | 所有对象共享一份,修改后影响所有对象 |
| 访问方式 | 类名::成员名(推荐) 或 对象.成员名(需权限允许) |
| 权限约束 | 受 public/private/protected 限制(如private静态成员仅类内访问) |
| 不可修饰 | 不能用 const 修饰(静态成员本身是类级别的,const是对象级别的只读) |
静态成员方法
语法规则
- 声明时加
static,定义时不加static; - 无隐含
this指针(不依赖具体对象)。
注意事项
- 只能调用其他静态成员(静态方法/静态数据),不能访问非静态成员(依赖对象的
this指针); - 不能被
const修饰(无this指针,无法保证对象只读)。
拓展
静态成员的内存布局
- 普通成员:存储在对象内部(栈/堆),每个对象一份;
- 静态成员:存储在静态数据区,整个程序生命周期内唯一,仅一份。
静态成员的线程安全问题
- 多线程环境下,静态成员的修改可能导致数据竞争;
- 解决方案:使用互斥锁(
std::mutex)保护静态成员的读写操作。
静态成员的初始化顺序
- 同一类内:按声明顺序初始化;
- 不同类间:初始化顺序不确定(避免在静态成员中依赖其他类的静态成员)。
类对象成员
类的成员可以是另一个类的对象,用于表达事物间的has-a(包含)关系(如汽车包含引擎、房子包含厨房)。
语法基础
// 被包含的类(引擎)
class Engine {
public:
string brand; // 品牌
float displacement; // 排量
// 引擎启动方法
void start() {
cout << brand << "引擎启动,排量:" << displacement << endl;
}
};
// 包含类(汽车)
class Car {
private:
unsigned int VIN; // 车架号(自身属性)
Engine engine; // 类对象成员(包含的属性)
public:
// 构造函数:通过初始化列表初始化对象成员
Car(unsigned int vin, string b, float d) : VIN(vin), engine{b, d} {}
// 汽车漂移方法(依赖引擎)
void drift() {
engine.start();
cout << "车架号" << VIN << "的汽车正在漂移!" << endl;
}
};
int main() {
Car myCar(123456, "丰田", 2.0);
myCar.drift();
return 0;
}
事物间的三大核心关系(OOP基础)
| 关系类型 | 含义 | 实现方式 | 示例 |
|---|---|---|---|
| is-a(从属) | 子类是父类的一种 | 类的继承 | 猫 → 哺乳动物、学生 → 人类 |
| has-a(包含) | 一个事物包含另一个事物 | 类对象成员 | 汽车 → 引擎、房子 → 厨房 |
| use-a(使用) | 一个事物使用另一个事物 | 友元/函数参数 | 遥控器 → 电视、司机 → 汽车 |
关键注意事项
初始化次序
- 类对象成员的初始化次序,取决于其在类中的声明顺序,与初始化列表中的顺序无关;
- 若成员间有依赖关系(如B依赖A初始化),需确保声明顺序为A在前、B在后。
初始化要求
- 类对象成员若无默认构造函数(无参/全默认参数),必须在初始化列表中显式初始化;
- 若有默认构造函数,可省略初始化(系统自动调用默认构造)。
拓展:has-a 与 is-a 的选择原则
- 当需要复用另一个类的功能(而非属性)时,优先用 has-a(组合);
- 当需要复用另一个类的属性+功能,且存在明确的“从属关系”时,用 is-a(继承);
- 示例:“鸟会飞” → 鸟(is-a)动物,鸟(has-a)翅膀(翅膀是属性,飞是依赖翅膀的功能)。
初始化列表
构造函数的特殊语法,专门用于成员数据的初始化,效率高于构造函数体内部赋值。
语法格式
class 类名 {
成员1;
成员2;
public:
类名(参数列表) : 成员1(值1), 成员2(值2), ... {
// 构造函数体(无需再初始化const/对象成员)
}
};
核心特点与适用场景
| 适用场景 | 说明 |
|---|---|
| const 成员 | 必须通过初始化列表初始化(无法在构造函数体赋值) |
| 类对象成员 | 无默认构造时必须显式初始化,有默认构造时推荐用(效率更高) |
| 普通成员 | 初始化效率高于构造函数体赋值(直接初始化,而非先默认构造再赋值) |
| 引用成员 | 必须通过初始化列表初始化(引用一旦绑定不可修改) |
示例代码(综合场景)
class Score { // 成绩类(类对象成员)
public:
int math;
int english;
// 无默认构造函数(必须显式初始化)
Score(int m, int e) : math(m), english(e) {}
};
class Student {
private:
const int ID; // const成员
string name; // 普通成员
Score score; // 类对象成员(无默认构造)
int& ageRef; // 引用成员
public:
// 初始化列表:初始化所有成员
Student(int id, string n, int m, int e, int& age)
: ID(id), name(n), score(m, e), ageRef(age) {
// 错误:const成员不能在体内赋值
// this->ID = id;
}
};
int main() {
int age = 18;
Student Jack(001, "Jack", 90, 85, age);
return 0;
}
拓展:初始化列表的效率原理
- 构造函数体赋值:成员先默认初始化(如string默认是空串),再赋值(覆盖默认值),两次操作;
- 初始化列表:直接调用成员的构造函数初始化,一次操作,无冗余步骤。
构造与析构次序
类(含对象成员)的构造和析构遵循固定规则,是内存管理的核心知识点。
构造次序
- 先构造所有类对象成员(按声明顺序,与初始化列表无关);
- 再构造当前类对象(执行构造函数体)。
示例验证
#include <iostream>
using namespace std;
class A {
public:
A() { cout << "构造 A" << endl; }
};
class B {
public:
B() { cout << "构造 B" << endl; }
};
class Node {
A a; // 声明顺序:A在前
B b; // 声明顺序:B在后
public:
// 初始化列表顺序与声明顺序相反,但构造次序仍按声明
Node() : b(), a() {
cout << "构造 Node" << endl;
}
};
int main() {
Node node;
// 输出结果:
// 构造 A
// 构造 B
// 构造 Node
return 0;
}
析构次序
与构造次序完全相反:
- 先析构当前类对象(执行析构函数体);
- 再析构所有类对象成员(按声明顺序的逆序)。
示例验证
#include <iostream>
using namespace std;
class A {
public:
~A() { cout << "析构 A" << endl; }
};
class B {
public:
~B() { cout << "析构 B" << endl; }
};
class Node {
A a;
B b;
public:
~Node() { cout << "析构 Node" << endl; }
};
int main() {
Node node;
// 输出结果(程序结束时析构):
// 析构 Node
// 析构 B
// 析构 A
return 0;
}
拓展:析构函数的核心作用
- 释放类对象占用的资源(如动态内存、文件句柄、网络连接);
- 类对象成员的析构由系统自动调用,无需手动处理。

浙公网安备 33010602011771号