前言
本文将进一步探讨类与对象的进阶特性,在已有构造函数、拷贝构造函数、析构函数、操作符重载和初始化列表的基础上,重点分析以下核心内容:类型转换、static 成员、友元函数、内部类、匿名对象。
C++ 类与对象进阶特性涵盖类型转换机制、静态成员变量与函数的存储访问规则、友元函数与类的权限突破、嵌套类的作用域独立性以及匿名对象的生命周期管理。重点解析构造函数隐式转换与 explicit 关键字的使用场景,静态成员的全局共享性与生命周期,友元关系的单向性,内部类作为独立作用域的特性,以及匿名对象在函数传参中的临时性应用。

本文将进一步探讨类与对象的进阶特性,在已有构造函数、拷贝构造函数、析构函数、操作符重载和初始化列表的基础上,重点分析以下核心内容:类型转换、static 成员、友元函数、内部类、匿名对象。
在 C++ 中,构造函数不仅仅是用来初始化对象的,它还定义了'如何将其他类型的数据转换为当前类类型'的规则。
**隐式转换原理:**如果一个自定义类中包含一个只接受一个参数的构造函数(或者除第一个参数外,其余参数都有默认值),那么编译器就可以把这个参数的类型,隐式地转换为该类的对象。
**代码演示场景:**你写了一个 Integer 类,它有一个接受 int 的构造函数。
#include <iostream>
using namespace std;
class Integer {
public:
// 带参数的构造函数:它定义了如何将 int 转为 Integer
Integer(int v) : value(v) {
cout << "构造函数被调用:int -> Integer" << endl;
cout << "value:" << value << endl;
}
private:
int value;
};
int main() {
// 显式调用:很正常
Integer a(10);
cout << "-------------------------------" << endl;
// 【隐式转换】:发生了什么?
// 编译器发现 '=' 右边是 int,左边是 Integer。
// 它自动查看 Integer 是否有接受 int 的构造函数。
// 找到了!于是编译器自动执行了 Integer(20)。
Integer b = 20;
return 0;
}
代码逻辑:
Integer b = 20在老标准中等价于Integer temp(20) + Integer b(temp)。 首先调用构造函数完成对临时对象 temp 的初始化 -> 调用拷贝构造函数将 temp 拷贝给对象 b(即先生成临时对象再进行拷贝构造)。 但在现代 C++ 编译器优化下,直接等同于Integer b(20)。
代码演示场景:在函数传参时的使用
#include <iostream>
using namespace std;
class Complex {
public:
// 单参数构造函数:定义了从 double 到 Complex 的转换规则
Complex(double r) : real(r), imag(0.0) {
cout << "调用转换构造函数:double -> Complex" << endl;
}
void print() {
cout << real << " + " << imag << "i" << endl;
}
private:
double real;
double imag;
};
void printComplex(Complex c) {
c.print();
}
int main() {
// 函数传参
// 函数需要 Complex 类型,但传入的是 double (5.1)
// 调用 Complex(double) 隐式转换,首先创建临时对象 Complex temp(5.1),再调用拷贝构造函数 Complex c(temp)
printComplex(5.1);
return 0;
}
虽然隐式转换很方便,但它有时会引发意外的错误或降低代码可读性。
例如:如果 Array(int size) 构造函数被隐式调用,Array a = 10 看起来像是把 a 赋值为 10,但实际上它的逻辑可能是创建了一个大小为 10 的数组,这很容易产生误解。
代码示例:禁止隐式类型转换
class MyString {
public:
// 使用 explicit 关键字修饰构造函数
explicit MyString(int n) : length(n) {}
private:
int length;
};
void func(MyString s) {}
int main() {
// MyString s1 = 10; // error:不允许隐式转换
// func(10); // error:不允许隐式转换参数
// 必须使用'显式'转换(直接初始化或强制转换)
MyString s2(10); // 直接初始化
MyString s3 = MyString(10); // 显式调用构造函数
func(static_cast<MyString>(10)); // 显式强制转换
return 0;
}
**温馨提示:**对于单参数构造函数,除非你有非常明确的理由允许隐式转换(例如数学类型 Complex 从 double 转换),否则通常建议加上 explicit 以增加类型安全性。
如果一个构造函数有多个参数,但除了第一个参数外,其余参数都有默认值,它依然被视为'转换构造函数',可以触发隐式转换。
class Data {
public:
// 这种也可以触发隐式转换:int -> Data
Data(int a, int b = 0) {}
};
int main() {
Data d = 5; // 合法,等同于 Data(5, 0)
return 0;
}
在 C++11 之后,如果构造函数接受多个参数,可以使用花括号 {} 进行隐式转换(这被称为列表初始化),explicit 同样可以禁止这种行为。
class Point {
public:
Point(int x, int y) {}
};
void draw(Point p) {}
int main() {
// C++11 允许:
Point a = { 5, 5 }; // 隐式转换 {int, int} -> Point
draw({ 1, 2 }); // 隐式转换 {int, int} -> Point
return 0;
}
**场景示例:*C++ 标准库的 string 就是最好的例子,它的构造函数接受 const char,允许隐式转换。
#include <iostream>
#include <string>
using namespace std;
void printMessage(const string& msg) {
cout << "Message: " << msg << endl; // 可以调用 .length(), .find() 等方法
}
int main() {
// 实际传入的是 const char*,编译器自动调用 string(const char*) 转换为 string 对象
printMessage("Hello World");
// 场景:赋值
string s = "C++";
return 0;
}
**场景示例:**假设你写了一个处理超大整数的类 BigInt
class BigInt {
public:
BigInt(int n) { /* 将 int 转为内部存储格式 */ }
// 通过友元重载加法:只定义 BigInt + BigInt
friend BigInt operator+(const BigInt& lhs, const BigInt& rhs) { /* 实现相加逻辑 */; }
};
int main() {
BigInt a = 100; // 场景:混合运算
// 编译器发现 operator+ 需要两个 BigInt,而右边是 int (50)。
// 它会自动调用 BigInt(50) 构造临时对象,然后进行相加。
BigInt b = a + 50;
return 0;
}
在 C++ 中,静态成员变量是一种特殊的类成员,与普通的成员变量不同,它们不属于类的某个具体对象,而是属于整个类。
这意味着,无论你创建了多少个该类的对象,静态成员变量在内存中只有一份拷贝,所有对象共享这一份数据。
这是最容易出错的地方,静态成员变量通常需要在类内声明,在类外定义 / 初始化。
在类内部使用 static 关键字进行声明。
class MyClass {
private:
static int sharedCount; // 只是声明,还没有分配内存
};
必须在类外部进行定义和初始化,这时才会分配内存。
// 必须带上类作用域
int MyClass::sharedCount = 0; // 定义并初始化
注意:
static 关键字。两种常见的方式访问静态成员变量:
MyClass::sharedCountobj.sharedCount请注意:静态成员作为类的成员,同样受 public、protected、private 访问权限的限制。
核心规则:
- Public: 可以通过 ClassName::value 在任何地方访问。
- Private: 不能在类外通过 ClassName::value 访问(除非是友元)。
- Protected: 不能在类外通过 ClassName::value 访问(除非是子类)。
代码验证:访问不同限制的静态成员
#include <iostream>
using namespace std;
class Server {
private:
// 私有静态成员:逻辑上只有 Server 内部能改
static int port;
public:
// 公有静态成员:谁都能改
static int status;
};
// 初始化静态成员
int Server::port = 8080; // 这里虽然是类外,但是是'定义',是被允许的特殊情况
int Server::status = 1;
int main() {
// 1. 访问 Public 静态成员 -> 成功
cout << "服务器状态:" << Server::status << " (允许)" << endl;
Server::status = 0; // 修改也允许
// 2. 试图访问 Private 静态成员 -> 失败
// cout << Server::port << endl; // <--- 编译报错!
// Server::port = 9090; // <--- 编译报错!
return 0;
}
定义如下:用 static 修饰的成员函数,称之为静态成员函数,静态成员函数没有 this 指针。
核心特征:
验证代码示例:
#include <iostream>
using namespace std;
class RuleTester {
public:
// === 资源准备 ===
static int staticVar; // 静态变量 (大家共享)
int normalVar; // 非静态变量 (对象独有)
RuleTester(int v) : normalVar(v) {}
static void helperStatic() {}
static void staticFunc() {
cout << "[静态函数] 正在运行" << endl;
// 1. 访问静态成员 -> 【成功】
// 因为 staticVar 存在于全局区,不需要对象就能找到
cout << " -> 访问 staticVar: " << staticVar << " (成功)" << endl;
// 2. 调用其他静态函数 -> 【成功】
helperStatic(); // 假设有这个函数,完全合法
// 3. 访问非静态成员 -> 【失败!】
// 编译器不知道我在指哪一个对象的 normalVar
// cout << normalVar << endl; // <--- 取消注释报错 error C2597: 对非静态成员'RuleTester::normalVar'的非法引用
// 4. 使用 this 指针 -> 【失败!】
// 静态函数没有 this 指针
// cout << this << endl; // <--- 取消注释报错 error C2355: 'this': 只能在非静态成员函数或非静态数据成员初始值设定项的内部引用
}
void normalFunc() {
cout << "[普通函数] 正在运行 (对象:" << this << ")" << endl;
// 1. 访问非静态成员 -> 【成功】
// 隐含了 this->normalVar
cout << " -> 访问 normalVar: " << normalVar << " (成功)" << endl;
// 2. 访问静态成员 -> 【成功】
// 普通函数完全有权限读取共享数据
cout << " -> 访问 staticVar: " << staticVar << " (成功)" << endl;
// 3. 调用静态函数 -> 【成功】
staticFunc(); // 普通函数可以随意调用静态函数
}
};
// 类外定义静态变量
int RuleTester::staticVar = 100;
int main() {
// 场景 1: 直接调用静态函数 (不需要对象)
std::cout << "=== 场景 1: 静态函数调用示例 ===" << std::endl;
RuleTester::staticFunc();
std::cout << "\n=== 场景 2: 对象函数调用示例" << std::endl;
RuleTester obj(999);
obj.normalFunc();
return 0;
}
代码示例:验证静态成员变量的唯一性,实例对象的共享性。
class Box {
public:
Box(int id) : _id(id) { }
static int sharedValue;
int _id;
};
int Box::sharedValue = 0;
int main() {
Box box1(1);
Box box2(2);
cout << "=== 验证 1: 内存地址对比 (最直接的证据) ===" << endl;
// 获取并打印静态变量的地址
cout << "box1.sharedValue 的地址:" << &box1.sharedValue << endl;
cout << "box2.sharedValue 的地址:" << &box2.sharedValue << endl;
// 对比普通变量的地址(它们应该是不同的)
cout << "box1.id (普通变量) 的地址:" << &box1._id << " (属于 box1)" << endl;
cout << "box2.id (普通变量) 的地址:" << &box2._id << " (属于 box2)" << endl;
if (&box1.sharedValue == &box2.sharedValue) {
cout << "-> 结论:地址相同,证明在内存中是同一份拷贝。" << endl;
}
cout << "\n=== 验证 2: 修改值行为测试 ===" << endl;
cout << "初始状态:" << std::endl;
cout << "box1 看 sharedValue = " << box1.sharedValue << endl;
cout << "box2 看 sharedValue = " << box2.sharedValue << endl;
cout << "\n[操作] 通过 box1 修改 sharedValue 为 999..." << endl;
box1.sharedValue = 999;
cout << "修改后:" << endl;
cout << "box1 看 sharedValue = " << box1.sharedValue << endl;
cout << "box2 看 sharedValue = " << box2.sharedValue << " <-- 也就是这里变了!" << endl;
return 0;
}
代码示例:验证静态成员不储存在对象的内存空间(堆 或 栈)
#include <iostream>
using namespace std;
// 类 A:只有一个 int 普通成员
class ClassWithoutStatic {
public:
int a; // 4 字节
};
// 类 B:有一个 int 普通成员 + 一个 static int 成员
class ClassWithStatic {
public:
int a; // 4 字节
static int b; // 逻辑上属于类,物理上不占对象空间
};
// 类 C:只有静态成员,没有普通成员
class OnlyStatic {
public:
static int c;
};
// 即使没有成员变量,C++ 标准规定空对象大小至少为 1
class Empty { };
int main() {
ClassWithoutStatic obj1;
ClassWithStatic obj2;
OnlyStatic obj3;
Empty obj4;
cout << "======= Sizeof 验证实验 ========" << endl;
cout << "int 的大小:" << sizeof(int) << " 字节" << endl;
cout << "\n1. 对比有无静态成员:" << endl;
cout << "ClassWithoutStatic (只有 int a): " << sizeof(obj1) << " 字节" << endl;
cout << "ClassWithStatic (int a + static int b): " << sizeof(obj2) << " 字节" << endl;
cout << "\n2. 极端情况验证:" << endl;
cout << "OnlyStatic (只有 static int): " << sizeof(obj3) << " 字节" << endl;
cout << "Empty (空类): " << sizeof(obj4) << " 字节" << endl;
if (sizeof(obj1) == sizeof(obj2) && sizeof(obj3) == sizeof(obj4)) {
cout << "\n-> 结论验证成功:静态成员变量不占用对象的内存空间。" << endl;
} else {
cout << "\n-> 结论验证失败。" << endl;
}
return 0;
}
代码示例:验证静态成员变量独立于对象生命周期
#include <iostream>
#include <string>
using namespace std;
// 监控工具类
class Tracer {
public:
Tracer(const string& n) : _name(n) { cout << "[内存分配] " << _name << " 已构造 " << endl; }
~Tracer() { cout << "[内存释放] " << _name << " 已析构 " << endl; }
private:
string _name;
};
// 测试类
class Box {
public:
// 这是一个静态成员,类型是 Tracer
static Tracer sharedResource;
Box() { cout << " -> Box 普通对象被创建 (构造函数)" << endl; }
~Box() { cout << " -> Box 普通对象被销毁 (析构函数)" << endl; }
};
// 定义并初始化静态成员
// 注意:这行代码在全局作用域,它会在 main 函数执行前运行!
Tracer Box::sharedResource("静态成员变量 (Static)");
int main() {
cout << "\n========== main 函数开始 ==========" << endl;
cout << "准备进入局部作用域创建对象..." << endl;
{
Box localBox; // 创建一个 Box 对象
cout << "局部 Box 对象正在使用..." << endl;
}
// localBox 在这里被销毁
cout << "局部作用域已结束,Box 对象已死。" << endl;
cout << "但请注意:静态成员还活着" << endl;
cout << "========== main 函数结束 ==========\n" << endl;
return 0;
}
经典样例:利用静态成员变量来统计当前有多少个对象存活。
#include <iostream>
using namespace std;
class Student {
public:
// 1. 声明静态成员变量
static int studentCount;
Student() {
// 每次创建对象,计数 +1
++studentCount;
}
~Student() {
// 每次销毁对象,计数 -1
--studentCount;
}
};
// 2. 定义并初始化静态成员变量 (通常在全局作用域)
int Student::studentCount = 0;
void test() {
Student s3;
cout << "局部对象 s3 存在时:" << Student::studentCount << endl; // 输出 3
}
int main() {
// 此时还没有对象,但静态变量已经存在
cout << "初始人数:" << Student::studentCount << endl; // 输出 0
Student s1;
Student s2;
cout << "创建两个对象后:" << Student::studentCount << endl; // 输出 2
test();
// s3 销毁后
cout << "局部对象 s3 销毁后,对象个数:" << Student::studentCount << endl;
return 0;
}
C++ 中的友元 (Friend) 机制能够突破封装限制,使特定函数或类获得访问其他类私有 (private) 和保护 (protected) 成员的权限。
友元函数的特性:
friend 关键字。使用场景:
<< (输出运算符) 或 >> (输入运算符),通常需要访问类的私有数据,但这些运算符的左操作数通常是 cout 或 cin,而不是对象本身,所以必须定义为非成员函数(友元)。**代码示例一:**假设有一个 Box 类,它的宽度是私有的,但我们想写一个外部函数来打印这个宽度。
#include <iostream>
using namespace std;
class Box {
double width; // 私有成员,默认 private
public:
// 构造函数
Box(double w) : width(w) {}
// 声明友元函数
// 注意:printWidth 不是 Box 的成员函数!
friend void printWidth(const Box& box);
};
// 友元函数的定义(不需要 Box:: 作用域解析符)
void printWidth(const Box& box) {
// 因为是友元,所以可以直接访问 box.width
cout << "Box 的宽度是:" << box.width << endl;
}
int main() {
Box box(10.0);
// 直接调用友元函数
printWidth(box);
return 0;
}
**代码示例二:**定义一个复数类 Complex,包含实部 real 和虚部 imag(均为私有),并通过友元重载输入输出。
#include <iostream>
using namespace std;
class Complex {
//【关键点 1】声明友元
// 这里的 friend 意味着这两个外部函数可以访问 Complex 的 private 成员
friend ostream& operator<<(ostream& out, const Complex& c);
friend istream& operator>>(istream& in, Complex& c);
public:
// 构造函数
Complex(int r = 0, int i = 0) : real(r), imag(i) {}
private:
int real;
int imag;
};
// 【关键点 2】重载输出运算符 <<
// 返回 ostream& 是为了支持链式调用 (如 cout << c1 << c2)
ostream& operator<<(ostream& out, const Complex& c) {
// 因为是友元,直接访问私有成员 real 和 imag
out << "(" << c.real << " + " << c.imag << "i)";
return out; // 必须返回 out 对象
}
// 【关键点 3】重载输入运算符 >>
// 注意:这里第二个参数不能加 const 修饰,因为我们要修改它
istream& operator>>(istream& in, Complex& c) {
cout << "请输入实部和虚部:";
// 直接写入私有成员
in >> c.real >> c.imag;
return in;
}
int main() {
Complex c1;
// 使用重载的 >>
// 相当于调用 operator>>(cin, c1);
cin >> c1;
// 使用重载的 <<
// 相当于调用 operator<<(cout, c1);
cout << "你输入的复数是:" << c1 << endl;
return 0;
}
细节深度解析:
- 参数顺序:
operator<<(ostream& out, const Complex& c)。第一个参数 out 对应操作符<<左边的cout。第二个参数 c 对应操作符<<右边的对象 c1。这就是为什么它不能是成员函数(成员函数要求左边必须是 this 也就是 Complex 对象)。- **返回类型 ostream&:**如果我们返回 void,那么
cout << c1可以工作,但cout << c1 << endl;会报错。因为cout << c1执行完变成 void,接下来的void << endl是非法的。返回 out 的引用,使得cout << c1执行后返回 cout 本身,接着执行cout << endl。- **输入时的引用:**在
operator>>中,参数Complex& c必须没有 const,因为输入的目的就是修改这个对象的值。
**核心概念:**如果在类 A 中将类 B 声明为'友元',都那么类 B 中的所有成员函数可以访问类 A 的私有和保护成员。
简单理解为:这就好比你(类 A)给了你最好的朋友(类 B)一把你家(私有区域)的备用钥匙,其他人进不来,但你的朋友可以自由进出。
**基本语法:**在需要'授权'的类中,使用 friend 关键字声明另一个类。
代码示例:声明 ClassB 是 ClassA 的友元
class ClassA {
// 声明 ClassB 是 ClassA 的友元
// 这意味着 ClassB 可以访问 ClassA 的所有私有/保护成员
friend class ClassB;
public:
ClassA(int val) : secretValue(val) {}
private:
int secretValue;
};
class ClassB {
public:
void accessSecret(const ClassA &a) {
// 直接访问 ClassA 的私有成员 secretValue
// 如果不是友元,这行代码会报错
cout << "ClassA 的私有数据是:" << a.secretValue << endl;
}
};
int main() {
ClassA a(10);
ClassB b;
b.accessSecret(a);
return 0;
}
**核心概念:**只将另一个类的某个特定成员函数声明为友元,而不是整个类,这需要严格注意声明顺序,使用较少。
**场景演示:**假设 类 A 中的成员函数 func() ,需要访问 类 B 的内部私有数据。
我们要解决的问题是:
- 在类 B 中,我们需要声明 类 A 的 func() 函数是友元函数。
- 编译器读到 类 A 的 func() 时,需要知道 类 B 存在。
- 编译器读到 类 B 的
friend声明时,需要知道 类 A 及其func()已经声明过。正确的实现步骤:
- **前向声明 类 B。**目的:告诉编译器 B 是一个类,因为 A 中会用到 B 的引用或指针。
- **定义 类 A,并声明 成员函数,但不要写函数体。**目的:让编译器知道 A 里面有个函数叫 func,但不能写函数体是因为此时 B 的内部细节(私有成员)还不可见。
- **定义 类 B,并在其中用
friend声明 A 的特定函数。**目的:此时编译器已经知道 A 和 func 的存在,所以 friend 声明是合法的。- **定义 类 A 成员函数的函数体。**目的:此时 A 和 B 都已完全定义,函数体中可以自由访问 B 的私有成员。
**代码示例:**假设我们有两个类:Date (日期类,包含私有数据) 和 DatePrinter (打印机类) ,我们需要让打印机类中的 printDate 成员函数访问 Date 类的私有数据。
#include <iostream>
using namespace std;
// 【第 1 步】前向声明 Date,因为 DatePrinter 需要用到它
class Date;
// 【第 2 步】定义 DatePrinter 类
// 注意:只声明 printDate 函数,千万不要在这里写函数体!
class DatePrinter {
public:
void printDate(const Date& d); // 此时编译器还不知道 Date 里的 private 成员
void otherFunc(const Date& d); // 这个函数不是友元,后续无法访问私有成员
};
// 【第 3 步】定义 Date 类
class Date {
private:
int year, month, day;
public:
Date(int y, int m, int d) : year(y), month(m), day(d) {}
// 关键时刻:声明 DatePrinter 的特定函数为友元
// 语法:friend 返回值类型 类名::函数名 (参数列表);
friend void DatePrinter::printDate(const Date& d);
};
// 【第 4 步】现在才开始写 DatePrinter 成员函数的具体实现
// 因为此时 Date 类已经完全定义,编译器知道 year/month/day 是什么
void DatePrinter::printDate(const Date& d) {
// 合法:因为是友元,可以访问 private 成员
cout << "Date: " << d.year << "-" << d.month << "-" << d.day << endl;
}
void DatePrinter::otherFunc(const Date& d) {
// 非法:otherFunc 不是友元,访问 d.year 会报错
// cout << d.year; // Error!
cout << "无法访问 Date 类的私有数据." << endl;
}
int main() {
Date dt(2023, 10, 1);
DatePrinter printer;
printer.printDate(dt); // 输出日期
printer.otherFunc(dt); // 输出无法访问的信息
return 0;
}
**核心概念:**在 C++ 中,内部类通常被称为嵌套类,它是指如果一个类定义在另一个类的内部,那么这个类就叫做内部类。
**温馨提示:**内部类是一个独立的类,跟定义在全局相比,他只是受外部类类域限制和访问限定符限制,所以外部类定义的对象中不包含内部类。
**代码示例:**定义外部类 Outer,在外部类 Outer 中定义嵌套类 Inner。
// 定义外部类 Outer
class Outer {
public:
// 外部类 Outer 的构造函数
Outer(int val) : outerData(val) { }
// 定义内部类 Inner
class Inner {
public:
// 内部类 Inner 的构造函数
Inner(int val) : innerData(val) {}
private:
int innerData = 200;
};
private:
int outerData = 100;
};
:: (例如 Outer::Inner)。代码示例:展示内部类的使用
#include <iostream>
using namespace std;
class Outer {
public:
// --- 1. 定义嵌套类 Inner ---
class Inner {
public:
void show() {
cout << "我是 Inner 类中的函数" << endl;
}
};
// --- 2. 在 Outer 内部使用 Inner ---
void funcInside() {
Inner in;
in.show();
}
// 返回值类型也可以直接写 Inner
Inner createInner() {
Inner temp;
return temp;
}
};
int main() {
// --- 3. 在 Outer 外部使用 Inner ---
// 错误写法:
// 编译器报错:'Inner' 未声明。
// 因为出了 Outer 的花括号,外面的世界根本不知道 Inner 是谁。
// Inner obj1;
// 正确写法:使用作用域解析运算符 ::
// 意思:我要找 Outer 里面的那个 Inner
Outer::Inner obj2;
obj2.show();
return 0;
}
关键点解析:
Inner in(在 Outer 内部):相当于在同一个文件夹下找文件,直接喊名字就行,不需要加前缀。Outer::Inner obj2(在 main 函数):相当于在根目录找深层文件夹里的文件,必须写出完整路径:文件夹/文件名。::运算符:它的全称就是'作用域解析运算符',专门用来剥开类的'外壳'取里面的东西。
public、protected 或 private 的,如果是 private,则只有外部类可以使用这个嵌套类。代码示例:展示访问权限对内部类的影响
#include <iostream>
using namespace std;
class Outer {
public:
// 1. 公有嵌套类 (Public Inner) -->任何人(包括 main 函数)都可以使用这个类名
class PublicInner {
public:
void show() {
cout << "我是 PublicInner,外面的人可以定义我的对象。" << endl;
}
};
private:
// 2. 私有嵌套类 (Private Inner) -->只有 Outer 自己知道这个类的存在
class PrivateInner {
public:
void show() {
cout << "我是 PrivateInner,只有 Outer 内部能用我。" << endl;
}
};
public:
// Outer 的成员函数:拥有最高权限
void internalUse() {
cout << "--- 进入 Outer 内部函数 ---" << endl;
// 1. 使用 PublicInner -> 没问题
PublicInner pub;
pub.show();
// 2. 使用 PrivateInner -> 没问题
PrivateInner pri;
pri.show();
}
};
int main() {
cout << "--- 在 main 函数中 ---" << endl;
// 场景 A:访问 Public 嵌套类
// 只要加上 Outer:: 前缀,外面完全可以使用
Outer::PublicInner pubObj;
pubObj.show();
// 场景 B:访问 Private 嵌套类
// 编译器会报错 error C2248: 'Outer::Inner': 无法访问 private class(在'Outer'类中声明)
// Outer::PrivateInner priObj; // <--- 取消注释这行会报错
// 场景 C:通过 Outer 的公开接口间接使用
Outer outerObj;
outerObj.internalUse(); // 这个函数在内部帮我们创建了 PrivateInner
return 0;
}
独立性:
this 指针,它就像是一个普通的类,只是名字被藏起来了。1. '仅仅是类名嵌套在作用域里' **含义:**这是一种组织代码的方式,而不是组合对象的方式,Outer 对于 Inner 来说,仅仅起到了一个命名空间的作用。
类比: 你有一个文件夹叫 Work(Outer)。 文件夹里有一个文件叫 Plan.doc(Inner)。 这个文件虽然放在文件夹里,但文件本身的内容并没有写着'我属于 Work 文件夹'。 文件夹本身也没有因为多了一个文件而变'重'(在 C++ 的类定义层面) **总结:**这两个类独立存在,只是内部类被外部类通过作用域限制了。
2. '外部类的对象不包含嵌套类的对象' **含义:**当你实例化 Outer 时,编译器不会自动帮你实例化 Inner,Outer 的内存大小(sizeof)只取决于 Outer 自己的成员变量,与 Inner 无关。
代码演示:外部类的对象不包含嵌套类的对象
#include <iostream>
using namespace std;
class Outer {
public:
int x; // 4 字节
// 定义嵌套类
class Inner {
public:
int y; // 4 字节
int z; // 4 字节
// Inner 的大小应该是 8 字节
};
};
int main() {
Outer out;
// 问:Outer 对象有多大?
// 如果 Outer 包含 Inner,大小应该是 4 + 8 = 12。
// 但实际上:
cout << "Outer 的内存大小:" << sizeof(out) << endl;
// 输出结果是 4!
// 证明:Outer 对象里根本没有 Inner 对象。
return 0;
}
3. '嵌套类的对象也不自动包含指向外部类对象的 this 指针'
**含义:**在 C++ 中,Inner 的对象是一个孤立的个体,没有一条'隐形的线'连向外部类对象。
这就意味着:Inner 对象没有指向 Outer 类的 this 指针,在 Inner 类中无法直接访问 Outer 类的非静态成员变量,它找不到那个变量属于哪个对象。
代码示例:内部类 Inner 访问 外部类 Outer,需要传入 外部类 Outer 的对象
class Outer {
public:
// 外部类的成员变量
int OuterData;
// 外部类的成员函数
void Func() {
cout << "外部类 Outer 的函数:Func() 被调用了!" << endl;
}
public:
// 外部类 Outer 的构造函数
Outer(int val) : OuterData(val) {}
// --- 定义嵌套类 ---
class Inner {
public:
// 注意:必须传入 Outer 的对象,因为 Inner 只是个独立的类
void inspect(Outer& out) {
cout << "=== Inner 正在访问 Outer 类 ===" << endl;
// 1. 访问外部类的 成员变量
cout << "外部类的成员变量:" << out.OuterData << endl;
// 2. 调用外部类的成员函数
out.Func();
}
};
};
int main() {
Outer out(100);
Outer::Inner obj;
obj.inspect(out);
return 0;
}
内部类默认是外部类的友元类。
代码演示:内部类访问外部类的私有成员
#include <iostream>
using namespace std;
class Outer {
private:
// --- 外部类的私有核心机密 ---
int secretCode;
void privateFunc() {
cout << "外部类 Outer 的私有函数:privateFunc() 被调用了!" << endl;
}
public:
// 外部类 Outer 的构造函数
Outer(int val) : secretCode(val) {}
// --- 定义嵌套类 ---
// Inner 默认就是 Outer 的友元,拥有最高访问权
class Inner {
public:
// 注意:必须传入 Outer 的对象,因为 Inner 只是个独立的类
void inspect(Outer& out) {
cout << "Inner 正在访问..." << endl;
// 1. 访问外部类的 private 变量
// 如果不是嵌套类,这行代码会报错
cout << "获取私有密码:" << out.secretCode << endl;
// 2. 调用外部类的 private 函数
out.privateFunc();
}
};
};
int main() {
Outer outerObj(999); // 创建一个外部对象
Outer::Inner innerObj; // 创建一个内部对象
innerObj.inspect(outerObj);
return 0;
}
在 C++ 中,匿名对象是指通过显式调用构造函数创建,但未被命名的临时对象。
Person("Bob") (没有名字,直接调用构造函数)Person p("Alice") (对象名为 p)代码示例:声明有名对象和匿名对象
class Person {
public:
Person(string name) : _name(name) { cout << _name << "调用构造函数" << endl; }
private:
string _name;
};
int main() {
Person p("Bob");
Person("Alice");
return 0;
}
**核心特性:**匿名对象的生命周期通常只存在于创建它的那一行代码(当前表达式语句),一旦该行代码执行完毕,匿名对象会立即调用析构函数销毁。
代码示例:匿名对象的生命周期和有名对象的生命周期
#include <iostream>
#include <string>
using namespace std;
class Person {
public:
// 构造函数:对象创建时调用
Person(string name) : m_Name(name) {
cout << " [构造] " << m_Name << " 出生了 (对象被创建)" << endl;
}
// 析构函数:对象销毁时调用
~Person() {
cout << " [析构] " << m_Name << " 挂掉了 (对象被销毁)" << endl;
}
private:
string m_Name;
};
int main() {
// 有名对象:Person p("Alice")
cout << "--- 开始测试有名对象 (Alice) ---" << endl;
Person p("Alice"); // 创建有名对象 p
cout << " Alice 依然存活" << endl;
// Alice 会一直活到当前函数结束
cout << "--- 有名对象测试结束 ---\n" << endl;
// 匿名对象:Person("Bob")
cout << "--- 开始测试匿名对象 (Bob) ---" << endl;
Person("Bob"); // 匿名对象:在这一行创建,也在这一行立即销毁
cout << "此时代码运行到这里,Bob 早就挂了" << endl;
cout << "--- 匿名对象测试结束 ---" << endl;
return 0;
}
如果一个对象只需要作为参数传递给函数,且之后不再使用,使用匿名对象可以减少代码行数,且不需要绞尽脑汁起变量名。
代码示例:
void doSomething(const string& s) {
cout << "Processing: " << s << endl;
}
int main() {
// 写法 1:有名对象
string str("Hello");
doSomething(str);
// 写法 2:匿名对象 (更简洁)
doSomething(string("Hello"));
return 0;
}
用于只需调用一次成员函数的场景。
代码示例:
class Solution {
public:
Solution() { /* */ }
void func() { /* */ }
};
int main() {
// 通过实名对象调用函数
Solution s;
s.func();
// 通过匿名对象调用函数
Solution().func();
return 0;
}
非 const 引用无法绑定匿名对象 C++ 规定:匿名对象是右值,普通的左值引用不能绑定右值
代码示例:
class A {
public:
A() { /* */ }
private:
int _val;
};
int main() {
// 报错!
// error C2440: '初始化': 无法从'A'转换为'A &'
// A& r = A();
}
const 引用可以绑定 (生命周期延长)
C++ 规定:如果一个匿名对象被绑定到一个 const 引用上,该匿名对象的生命周期会被延长,直到该引用变量的作用域结束。
代码示例:
class A {
public:
A() { /* */ }
private:
int _val;
};
int main() {
// 正确引用
const A& r = A();
}

微信公众号「极客日志」,在微信中扫描左侧二维码关注。展示文案:极客日志 zeeklog
将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online
将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online
将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML转Markdown在线工具,online
通过删除不必要的空白来缩小和压缩JSON。 在线工具,JSON 压缩在线工具,online
将JSON字符串修饰为友好的可读格式。 在线工具,JSON美化和格式化在线工具,online