
🫧个人主页:小年糕是糕手
💫个人专栏:《C++》《C++同步练习》《数据结构》《C语言》
🎨你不能左右天气,但你可以改变心情;你不能改变过去,但你可以决定未来!

目录
一、初始化列表
知识回顾:
- const 修饰的成员变量:常量成员一旦声明必须初始化,且无法在构造函数体中赋值,只能通过初始化列表完成初始化。
- 引用类型的成员变量:引用必须在定义时绑定到有效对象,构造函数体中赋值仅视为对引用指向对象的修改,而非绑定,因此需通过初始化列表初始化。
- 无默认构造函数的类类型成员:若成员所属类仅提供带参构造函数(无默认构造函数),无法通过默认方式创建该成员,必须在初始化列表中显式调用其带参构造函数。
- 基类(无默认构造函数时):派生类构造时,若基类无默认构造函数,需在派生类构造函数的初始化列表中调用基类的带参构造函数,完成基类部分的初始化。
静态成员变量比较特殊:
静态成员变量归属于类本身(而非对象),存储在全局 / 静态存储区,初始化核心规则如下:
静态成员类型 初始化位置 关键说明 普通静态成员(非 const / 非 inline) 类外部(必须放在.cpp 源文件) 禁止类内初始化;若放头文件会导致多文件重复定义,违反 ODR 规则 const static(算术 / 枚举类型,C++11+) 类内(可选)或类外 类内初始化后,若需取地址 / 显式实例化,仍需在类外仅声明(无需重复赋值) inline static(C++17+) 类内(可直接放头文件) 无需类外初始化,多文件包含无重复定义问题 核心要点
- 唯一初始化点 :静态成员必须保证全局仅初始化一次,因此普通静态成员务必放在
.cpp文件(而非头文件);- 访问权限不影响初始化:即使是 private 静态成员,也需在类外完成初始化(初始化不受访问控制限制);
- 默认初始化:若未显式初始化,静态成员会被 "零初始化"(如 int 型默认值为 0),但建议显式初始化保证可读性。
最简示例
cpp// 头文件(.h) class Test { public: static int num; // 普通静态成员(类内仅声明) static const int max = 100; // const static(C++11+类内初始化) inline static double pi = 3.14;// inline static(C++17+类内初始化) }; // 源文件(.cpp) int Test::num = 0; // 普通静态成员类外初始化 const int Test::max; // 可选:仅声明(类内已初始化,需取地址时加)大家记住核心的一点:静态成员要在类外进行初始化!
1、有一个类A,其数据成员如下: 则构造函数中,成员变量一定要通过初始化列表来初始化的是( )class A {
...
private:
int a;
public:
const int b;
float* &c;
static const char* d;
static double* e;
};
答案:b、c
解析:
a是内置类型成员,可在构造函数体中赋值初始化;无需强制用初始化列表,b有const修饰是常量成员变量,必须在初始化列表中初始化;c是引用类型成员(指针的引用),引用必须在定义时绑定对象,构造函数体中赋值无法完成绑定,必须通过初始化列表初始化;d和e都是静态成员变量,需要在类外部进行初始化,故答案为b、c
2、下面程序的运行结果是( )
cppclass A { public: A(int a) :_a1(a) ,_a2(_a1) {} void Print() { cout<<_a1<<" "<<_a2<<endl; } private: int _a2; int _a1; } int main() { A aa(1); aa.Print(); }答案:1和随机值
解析:
我们通过初始化列表我们知道**初始化顺序,由类中成员变量的声明顺序决定,而非初始化列表的书写顺序!**类A的私有成员声明顺序是:int _a2;→int _a1;所以我们定义变量并传值的时候会先给调用_a2,而_a2中是用_a1来初始化的,此时_a1并没有一个明确的值,所以是个随机值,所以_a2也是个随机值(用一个随机值进行赋值得到的就是一个随机值),此时再来初始化_a1,而_a1是用a来初始化的,而a的值是明确的,所以_a1就是a,即为1.
二、static变量
知识回顾:
本质:属于 "静态存储期" 的变量,存储在全局 / 静态存储区,生命周期贯穿整个程序(程序启动时初始化,结束时销毁),作用域由修饰位置决定。
核心目的 :控制变量的作用域 和存储期,实现 "跨函数 / 跨对象共享数据" 或 "限制数据访问范围"。
分类与基本用法:
全局作用域的 static 变量(文件级 static):
- 定义:在
.cpp文件的全局作用域前加static(如static int num = 0;)。- 特点:作用域限制在当前
.cpp文件(编译模块),其他文件无法访问,避免命名冲突。局部作用域的 static 变量(函数内 static):
- 定义:在函数 / 代码块内定义时加
static(如void func() { static int cnt = 0; cnt++; })。- 特点:仅初始化一次,函数多次调用时保留上次的值(跨函数调用共享状态)。
类的 static 成员变量:
- 定义:在类内声明时加
static(如class A { static int data; };),需在类外(.cpp)初始化。- 特点:属于类本身(非对象),所有对象共享该变量,可通过 "类名::变量名" 直接访问。
规则:
- 静态变量默认 "零初始化"(如
static int a;默认值为 0),局部 static 变量仅首次进入作用域时初始化。- 类的 static 成员变量:需在类外初始化(普通 static 成员放
.cpp,C++17 后inline static可类内初始化),访问权限受类的访问控制(public/private)限制。- 全局 static 变量与类的 static 成员变量:作用域不同(前者限文件,后者属于类),但存储期均为全局。
1、在一个cpp文件里面,定义了一个static类型的全局变量,下面一个正确的描述是()A.只能在该cpp所在的编译模块中使用该变量
B.该变量的值是不可改变的
C.该变量不能在类的成员函数中引用
D.这种变量只能是基本类型(如int,char)不能是C++类型
答案:A
解析:
A:全局作用域的
static变量,其作用域被限制在 "当前编译模块(即定义它的.cpp文件)" 内,其他文件无法通过外部链接访问该变量,因此该描述正确B:
static仅限制全局变量的作用域,不影响变量的可修改性(变量本身并非const类型),因此该描述错误。C:只要类的成员函数与该
static全局变量处于同一.cpp文件中,成员函数可以正常引用该变量,因此该描述错误。D:全局
static变量可以是任意 C++ 类型(包括自定义类、结构体等),并非只能是基本类型,因此该描述错误。
2、关于C++类中static 成员和对象成员的说法正确的是()A.static 成员变量在对象构造时生成
B.static 成员函数在对象成员函数中无法调用
C.static 成员函数没有this指针
D.static 成员函数不能访问static 成员变量
答案:C
解析:
A:static 成员变量属于类本身,在 ** 程序启动时(main 函数执行前)** 就已初始化生成,并非在对象构造时生成。
B:static 成员函数可以在对象的成员函数中被调用(可通过 "类名::函数名" 或 "对象。函数名" 的方式调用)。
C:static 成员函数是类的函数,不依赖具体对象,因此没有 this 指针(this 指针指向当前对象,而 static 函数与对象无关)。
D:static 成员函数的核心特性之一就是可以直接访问 static 成员变量(两者都属于类,不依赖对象)。
3、下面程序段包含4个函数,其中具有隐含this指针的是()int f1();
class T
{
public:static int f2();
private:friend int f3();
protect:int f4();
};
答案:f4()
解析:
全局函数不具备this指针,static函数不具备this指针,友元函数不具备this指针,类的普通成员函数具有隐藏的this指针;在 C++ 中,只有 "类的非静态成员函数" 具备 this 指针
三、友元
1. 本质
友元是打破类封装性的特殊机制,允许非类成员的函数 / 类 ,直接访问类的
private/protected成员(包括变量、函数),本质是 "封装的例外"。2. 核心目的
在不开放类成员访问权限的前提下,让特定外部实体(函数 / 类)能便捷访问类的私有资源(适用于运算符重载、跨类协作等场景)。
3. 分类与基本用法
友元类型 定义方式 关键说明 友元函数 类内声明: friend 返回值 函数名(参数);可是全局函数 / 其他类的成员函数;无 this 指针,需显式传对象 友元类 类内声明: friend class 类名;被声明的类的所有成员函数,均可访问当前类的私有成员 友元成员函数 类内声明: friend 返回值 类名::函数名(参数);仅其他类的指定成员函数可访问当前类私有成员(粒度更细)
cppclass A { private: int num = 10; // 1. 声明友元函数 friend void printNum(A& a); // 2. 声明友元类 friend class B; // 3. 声明友元成员函数(需先声明类C) class C; friend void C::func(A& a); }; // 友元函数:可访问A的private成员 void printNum(A& a) { cout << a.num << endl; // 合法 } // 友元类:B的所有成员可访问A的private成员 class B { public: void showA(A& a) { cout << a.num << endl; // 合法 } }; // 友元成员函数 class C { public: void func(A& a) { cout << a.num << endl; // 合法 } };4. 核心规则
- 单向性:A 声明 B 为友元 → B 可访问 A 的私有成员,但 A 不能访问 B 的私有成员(友元关系不互逆);
- 不可传递:A 是 B 的友元,B 是 C 的友元 → A 不是 C 的友元;
- 不影响访问权限 :友元只是 "获得访问权",并非类的成员函数,因此无 this 指针,需通过对象参数访问类成员;
- 声明位置无关:友元声明可放在类的 public/private/protected 任意位置,效果一致;
- 封装权衡:友元简化代码的同时破坏封装,需谨慎使用(仅用于必要场景)。
1、下面有关友元函数与成员函数的区别,描述错误的是()A.友元函数不是类的成员函数,和普通全局函数的调用没有区别
B.友元函数和类的成员函数都可以访问类的私有成员变量或者是成员函数
C.类的成员函数是属于类的,调用的时候是通过指针this调用的
D.友元函数是有关键字friend修饰,调用的时候也是通过指针this调用的
答案:D
解析:
A:友元函数不是类的成员函数,调用方式与普通全局函数一致(无需通过对象 / 类调用)
B:友元函数通过
friend声明获得访问类私有成员的权限,类的成员函数本身就可访问类的所有成员。C:类的成员函数属于类,调用时会隐含传递
this指针(指向当前调用对象)D:友元函数是用
friend修饰,但它不是类的成员函数,没有 this 指针,调用时也不会通过 this 指针关联对象(需显式传入对象参数来访问类成员)。
2、一个类的友元函数能够访问类的()A.私有成员
B.保护成员
C.公有成员
D.所有成员
答案:D
解析:
友元函数相当于就是当成了朋友,所以可以访问那个类里的所有成员,包括私有
