
🌟 个人主页 : 噜噜大王
🏆 道阻且长,行则将至
🎨噜噜大王的简介:

文章目录
- 前言
- 一、构造函数初始化列表:高效初始化的关键
- [1. 基本语法](#1. 基本语法)
- [2. 必须用初始化列表的 3 种成员](#2. 必须用初始化列表的 3 种成员)
- [3. 核心规则(易错点)](#3. 核心规则(易错点))
- [4. 经典例题解析](#4. 经典例题解析)
- 5.构造函数初始化顺序
- [二、类型转换:隐式转换与 explicit 限制](#二、类型转换:隐式转换与 explicit 限制)
- [1. 隐式转换规则](#1. 隐式转换规则)
- [2. explicit 关键字](#2. explicit 关键字)
- [3. 代码演示](#3. 代码演示)
- [三、static 成员:属于类,而非对象](#三、static 成员:属于类,而非对象)
- [1. static 成员变量](#1. static 成员变量)
- [2. static 成员函数](#2. static 成员函数)
- [3. 实战案例:统计对象个数](#3. 实战案例:统计对象个数)
- 四、友元:打破封装,谨慎使用
- [1. 友元函数](#1. 友元函数)
- [2. 友元类](#2. 友元类)
- [3. 代码演示](#3. 代码演示)
- 五、内部类:嵌套定义,专属关联
- 六、匿名对象:临时使用,用完即销毁
- 七、对象拷贝的编译器优化
- 总结
前言
在 C++ 面向对象编程中,类和对象的进阶特性是构建高效、安全代码的核心。上篇我们聊了类的基础定义与构造析构,今天深入初始化列表、类型转换、static 成员、友元、内部类、匿名对象六大核心知识点,结合代码案例与易错点解析,帮你彻底吃透这些高频考点。
一、构造函数初始化列表:高效初始化的关键
构造函数初始化列表是 C++ 推荐的成员初始化方式,相比函数体内赋值,它直接在成员定义阶段(初始化列表是成员变量定义的地方)完成初始化,效率更高,且能解决特殊成员的初始化问题。
1. 基本语法
以冒号:开头,逗号分隔成员初始化项,格式:
cpp
类名(参数)
: 成员1(值1)
, 成员2(值2)
{ 函数体 }
2. 必须用初始化列表的 3 种成员
- 引用成员:引用必须在定义时绑定变量,无法在函数体内赋值
- const 成员:常量成员只能初始化,不能赋值
- 无默认构造的自定义类型成员:无法默认构造,必须显式初始化
3. 核心规则(易错点)
初始化顺序 :按类中声明顺序初始化,和列表书写顺序无关(建议两者保持一致,避免 bug)
默认缺省值 :C++11 支持成员声明时给缺省值,未在列表显式初始化的成员会使用该值
强制初始化 :所有成员都会走初始化列表,未显式初始化的内置类型可能是随机值,自定义类型会调用默认构造(无则报错)
构造函数的初始化列表:每一个构造函数都无论是否显示写初始化列表,每个构造函数都有初始化列表,所以构造函数和析构函数一样无论显示写不写构造函数,都会去调用自定义类型成员变量的默认构造函数和析构函数。
4. 经典例题解析
cpp
class A {
public:
A(int a)
: _a1(a)
, _a2(_a1)
{}
void Print()
{
cout << _a1 << " " << _a2 << endl;
}
private:
int _a2 = 2; // 声明顺序:_a2在前
int _a1 = 2; // _a1在后
};
int main() {
A aa(1);
aa.Print(); // 输出:1 随机值(答案D)
}
解析:成员按声明顺序初始化,先初始化_a2(此时_a1未初始化,是随机值),再初始化_a1=1。
5.构造函数初始化顺序
初始化列表和默认缺省值只走一个(有初始化列标就走初始化列表,没有初始化列标就走默认缺省值),然后再去走构造函数函数体内的。

二、类型转换:隐式转换与 explicit 限制
C++ 支持内置类型→类类型 、类类型→类类型的隐式转换,由构造函数控制,explicit可禁止隐式转换。
1. 隐式转换规则
- 单参数构造函数:支持内置类型隐式转类对象
- 多参数构造函数:C++11 后支持{}列表隐式转换
- 拷贝构造函数:支持同类对象隐式转换
2. explicit 关键字
在构造函数前加explicit,仅保留显式初始化,禁止隐式转换:
cpp
class A {
public:
explicit A(int a1) : _a1(a1) {} // 禁止隐式转换
private:
int _a1;
};
int main() {
A aa1 = 1; // 编译报错!隐式转换被禁止
A aa2(1); // 正确,显式初始化
}
3. 代码演示
cpp
#include<iostream>
using namespace std;
class A
{
public:
//explicit A(int a = 0)
A(int a = 0)
{
_a1 = a;
}
A(const A& aa)
{
_a1 = aa._a1;
}
A(int a1, int a2)
:_a1(a1)
, _a2(a2)
{}
void Print() {
cout << _a1 << " " << _a2 << endl;
}
private:
int _a1;
int _a2;
};
class Stack
{
public:
void Push(const A& aa)
{
//...
}
private:
A _arr[10];
int _top;
};
int main()
{
A aa1(1);
aa1.Print();
// 隐式类型转换
// 2构造一个A的临时对象,再用这个临时对象拷贝构造aa2
// 编译器遇到连续构造+拷贝构造->优化为直接构造
A aa2 = 2;
aa2.Print();
A& raa1 = aa2;
const A& raa2 = 2;
int i = 1;
double d = i;
const double& rd = i;
Stack st;
A aa3(3);
st.Push(aa3);
st.Push(3);
// C++11
A aa5 = { 1, 1 };
const A& raa6 = { 2,2 };
st.Push(aa5);
st.Push({2,2});
return 0;
}

这同时也体现了const做形参的好处
三、static 成员:属于类,而非对象
用static修饰的成员(变量 / 函数),归类所有,所有对象共享,存储在静态区,无 this 指针。
1. static 成员变量
- 声明:类内声明,属于类域
- 初始化:必须类外初始化(不能在声明处给缺省值,因为缺省值是给构造函数初始化列表的,静态成员变量不属于某个对象,不走构造函数初始化列表。)
- 访问:类名::变量名 或 对象.变量名
- 限制:静态成员变量也是类的成员,受public、protected、private 访问限定符的限制。
2. static 成员函数
- 无 this 指针:只能访问 static 成员,不能访问非 static 成员
- 访问:类名::函数名() 或 对象.函数名()
- 限制:静态成员函数也是类的成员,受public、protected、private 访问限定符的限制。
- 类内访问:非静态的成员函数,可以访问任意的静态成员变量和静态成员函数。
3. 实战案例:统计对象个数
cpp
class A {
public:
A()
{
++_scount;
}
A(const A&)
{
++_scount;
}
static int GetCount()
{
return _scount;
}
private:
static int _scount; // 类内声明
};
int A::_scount = 0; // 类外初始化
int main() {
A a1, a2;
A a3(a1);
cout << A::GetCount(); // 输出:3
}
四、友元:打破封装,谨慎使用
友元(friend)用于突破类的访问权限,让外部函数 / 类访问私有成员,分友元函数 和友元类。
1. 友元函数
- 不是类成员函数,无 this 指针,友元函数仅仅是⼀种声明,他不是类的成员函数。
- 可访问类的私有 / 保护成员
- 声明位置任意,不受访问限定符限制
cpp
class B; // 前置声明
class A {
friend void func(const A&, const B&); // 友元声明
private:
int _a = 1;
};
class B {
friend void func(const A&, const B&);
private:
int _b = 2;
};
void func(const A& a, const B& b) {
cout << a._a << b._b; // 可访问私有成员
}
2. 友元类
- 友元类的所有成员函数都是当前类的友元(在A中friendB,B类中就可以访问A中的所有的成员)
- 单向性:A 是 B 的友元,B 不是 A 的友元
- 无传递性:A 友元 B,B 友元 C,A 不是 C 的友元
3. 代码演示
cpp
using namespace std;
class A
{
public: // 友元声明
friend class B;
void print() const
{
cout << "哈哈哈" << endl;
}
private:
int _a1 = 1;
int _a2 = 2;
};
class B
{
public:
void func1(const A& aa)
{
aa.print();
cout << aa._a1 << endl;
cout << _b1 << endl;
}
void func2(const A& aa)
{
cout << aa._a2 << endl;
cout << _b2 << endl;
}
private:
int _b1 = 3;
int _b2 = 4;
};
int main()
{
A aa;
B bb;
bb.func1(aa);
bb.func2(aa);
return 0;
}
注意事项 :友元破坏封装、增加耦合度,仅在必要场景(如运算符重载)使用,不可滥用。
五、内部类:嵌套定义,专属关联
定义在另一个类内部的类,称为内部类,是独立类,仅受外部类域和访问权限限制。
核心特性
- 内部类默认是外部类的友元类,可访问外部类私有成员
- 外部类不能直接访问内部类私有成员
- 内部类是⼀个独立的类,跟定义在全局相比,他只是受外部类类域限制和访问限定符限制,所以外部类定义的对象中不包含内部类。
- 内部类仅在外部类域内可见,private 内部类为外部类专属
cpp
using namespace std;
class A
{
private:
static int _k;
int _h = 1;
public:
class B // B默认就是A的友元
{
public:
void foo(const A& a)
{
cout << _k << endl;
//OK
cout << a._h << endl;
//OK
}
private:
int _b1;
};
void test()
{
//_b1 = 2;err外部类不可以直接访问内部类,受访问限定符约束
}
};
int A::_k = 1;
int main()
{
cout << sizeof(A) << endl;//4
A::B b;
A aa;
b.foo(aa);//1 1
return 0;
}
六、匿名对象:临时使用,用完即销毁
格式:类名(实参),无对象名,生命周期仅当前一行,适合临时调用成员函数。
特性与用法
- 生命周期短:创建后立即销毁
- 节省空间:无需定义有名对象,临时场景高效
- 常见场景:
类名().成员函数()
cpp
using namespace std;
class A {
public:
A()
{
cout << "创建";
}
void func()
{
cout << "666" << endl;
}
~A()
{
cout << "销毁";
}
};
int main() {
A(); // 匿名对象:输出"创建销毁"
A().func(); // 临时调用成员函数
}
七、对象拷贝的编译器优化
现代编译器会优化连续构造 + 拷贝构造 ,减少拷贝开销(如A aa = 1直接构造,不生成临时对象)。
cpp
using namespace std;
class A
{
public:
A(int a = 0)
:_a1(a)
{
cout << "A(int a)" << endl;
}
A(const A& aa)
:_a1(aa._a1)
{
cout << "A(const A& aa)" << endl;
}
A& operator=(const A& aa)
{
cout << "A& operator=(const A& aa)" << endl;
if (this != &aa)
{
_a1 = aa._a1;
}
return *this;
}
~A()
{
cout << "~A()" << endl;
}
private:
int _a1 = 1;
};
void f1(A aa)
{
}
A f2()
{
A aa;
return aa;
}
int main()
{
// 隐式类型,连续构造+拷⻉构造->优化为直接构造
f1(1);
// ⼀个表达式中,连续构造+拷⻉构造->优化为⼀个构造
f1(A(2));
//返回时⼀个表达式中,连续拷⻉构造+拷⻉构造->优化⼀个拷⻉构造 (vs2019 debug)
A aa1 = f2();
return 0;
}
优化场景
- 隐式类型转换:
f1(1)直接构造,跳过拷贝 - 匿名对象传参:
f1(A(2))合并为一次构造 - 返回值优化:
A aa = f2()直接构造,无临时对象
linux下可以将下面代码拷贝到test.cpp文件,编译时用 g++ test.cpp -fno-elide-constructors 的方式关闭构造相关的优化。
具体怎么优化要取决于编译器,不同的编译器上优化方式会不同

总结
C++ 类和对象的进阶特性,核心是初始化列表控效率、static 管共享、友元破封装、内部类强关联、匿名对象省空间。这些知识点既是面试高频考点,也是编写高质量 C++ 代码的基础,建议结合案例多练,吃透细节与易错点。
以上就是该篇博客的内容了,如果内容存在不足,请大佬多多包涵并不吝赐教,会在写出优秀好文的路上努力拼搏达,感谢支持!!!
一键三连。[1](#1)感谢佬们的支持😄
- 点赞,关注,收藏qwq感谢佬佬们 ↩︎