一、初始化列表
初始化列表的定义
我们之前在学构造函数中提到,如果自定义类型没有默认构造函数,在进行初始化时就要用到初始化列表,初始化列表的使用方法是以冒号开始,以逗号分隔,每个成员变量后面跟一个括号,括号中是初始值或表达式。
初始化列表的特点:
- 每个成员变量只能在初始化列表中出现一次,语法意义上可以认为初始化列表是每个成员变量定义初始化的地方。
- 没有默认构造函数的自定义成员变量,引用成员变量,const 成员变量必须在初始化列表中初始化,否则会编译报错。
- C++11 规定可以在声明时给定缺省值,给没有显示在初始化列表初始化的成员使用。
- 尽量使用初始化列表进行初始化,因为不在初始化列表的成员也会走初始化列表,如果这个成员在声明位置给定了缺省值,就用缺省值进行初始化,如果没有给定,对于内置类型,编译器可能会初始化也可能不会,C++ 没有明确规定,对于自定义成员变量,则会去调用它的默认构造函数,如果没有默认构造函数,则会编译报错。
- 初始化列表中按照成员变量在类中声明顺序进行初始化,跟成员在初始化列表出现的先后顺序无关。建议声明顺序和初始化列表顺序保持一致。
cpp
#include <iostream>
using namespace std;
class Time
{
public:
Time(int hour)
:_hour(hour)
{
cout << "Time()" << endl;
}
private:
int _hour;
};
class Date
{
public:
Date(int x, int year, int month, int day)
:_year(year)
, _month(month)
, _day(day)
,_t(12)
,_ref(x)
,_n(1)
{
}
void Print() const
{
cout << _year << "-" << _month << "-" << _day << endl;
}
private:
//注意这里不是初始化,是给缺省值,在初始化列表没有显示初始化时就用这个缺省值初始化
int _year = 1;
int _month = 1;
int _day;
//这三种情况必须在初始化列表初始化
Time _t = 12; //没有默认构造函数
int& _ref; //引用
const int _n = 1; //const
};

二、类型转换
C++ 支持内置类型隐式转换为类类型对象,需要有以内置类型为参数的构造函数支持,构造函数前面加 explicit 就不再支持隐式类型转换。类类型的对象之间也可以隐式转换,需要相应的构造函数支持。
cpp
class A
{
public:
//explicit A(int a)
A(int a)
:_a1(a)
{
}
//explicit A(int a1, int a2)
A(int a1, int a2)
:_a1(a1)
, _a2(a2)
{
}
void Print()
{
cout << _a1 << " " << _a2 << endl;
}
int Get() const
{
return _a1 + _a2;
}
private:
int _a1;
int _a2;
};
class B
{
public:
B(const A& a)
:_b(a.Get())
{}
private:
int _b = 0;
};
int main()
{
//Date d1;
//d1.Print();
A aa1 = 1;
const A& aa2 = 1;
A aa3 = { 2,2 };
B b = aa3;
const B& rb = aa3;
return 0;
}
三、static 成员
- 用 static 修饰的成员变量称为静态成员变量,在类内声明,类外初始化,静态成员变量为所有类对象所共享,不属于某个单独的对象,不存在对象中,存在静态区,静态成员变量不能在声明处给缺省值,因为缺省值是给构造函数初始化列表的,静态成员函数不属于某个对象,不走构造函数初始化列表。
- 用 static 修饰的成员函数称为静态成员函数,静态成员函数没有 this 指针,静态成员函数可以访问其他静态成员,但不可以访问非静态,因为没有 this 指针。非静态成员函数可以任意访问静态成员变量和静态成员函数。
- 突破类域就可以访问静态成员,可以通过 :: 成员变量或对象 . 成员变量来访问静态成员变量和静态成员函数。
- 静态成员也是类的成员,受 public、protected、private 访问限定符的限制。
cpp
class A
{
public:
A()
{
++_scount;
}
A(const A& t)
{
++_scount;
}
~A()
{
--_scount;
}
static int GetACount()
{
return _scount;
}
private:
// 类里面声明
static int _scount;
};
// 类外面初始化
int A::_scount = 0;
int main()
{
cout << A::GetACount() << endl;
A a1, a2;
A a3(a1);
cout << A::GetACount() << endl;
cout << a1.GetACount() << endl;
// 编译报错:error C2248: "A::_scount": 无法访问 private 成员(在"A"类中声明)
//cout << A::_scount << endl;
return 0;
}
四、友元
友元的概念
友元(friend)是 C++ 提供的一种突破类封装性的机制。它允许一个函数或另一个类访问当前类的私有(private)和保护(protected)成员。友元关系是单向的、非传递的,且不能被继承。
友元的三种形式
- 友元函数:一个普通函数被声明为类的友元,可以访问该类的私有和保护成员。
- 友元类:一个类被声明为另一个类的友元,则该类的所有成员函数都可以访问另一个类的私有和保护成员。
- 友元成员函数:一个类的某个成员函数被声明为另一个类的友元,只有这个特定的成员函数可以访问另一个类的私有和保护成员。
友元函数示例
cpp
#include <iostream>
using namespace std;
class Box {
private:
int width;
int height;
public:
Box(int w, int h) : width(w), height(h) {}
// 声明友元函数
friend void printBox(const Box& box);
};
// 友元函数定义
void printBox(const Box& box) {
// 可以直接访问私有成员
cout << "Width: " << box.width << endl;
cout << "Height: " << box.height << endl;
}
int main() {
Box b(10, 20);
printBox(b); // 输出: Width: 10, Height: 20
return 0;
}
友元类示例
cpp
#include <iostream>
using namespace std;
class A {
private:
int secret = 42;
// 声明 B 为友元类
friend class B;
};
class B {
public:
void showSecret(const A& a) {
// B 可以访问 A 的私有成员
cout << "A's secret: " << a.secret << endl;
}
};
int main() {
A a;
B b;
b.showSecret(a); // 输出: A's secret: 42
return 0;
}
友元成员函数示例
cpp
#include <iostream>
using namespace std;
class B; // 前向声明
class A {
private:
int value = 100;
// 声明 B 类的 display 函数为友元
friend void B::display(const A& a);
};
class B {
public:
void display(const A& a) {
// 只有这个函数可以访问 A 的私有成员
cout << "A's value: " << a.value << endl;
}
void cannotAccess(const A& a) {
// 这个函数不能访问 A 的私有成员
// cout << a.value << endl; // 编译错误
}
};
int main() {
A a;
B b;
b.display(a); // 输出: A's value: 100
return 0;
}
友元的特点和注意事项
- 单向性:A 是 B 的友元,不代表 B 是 A 的友元。
- 非传递性:A 是 B 的友元,B 是 C 的友元,不代表 A 是 C 的友元。
- 不能被继承:友元关系不能被派生类继承。
- 破坏封装:友元破坏了类的封装性,应谨慎使用。
- 声明位置:友元声明可以在类的任何位置(public、private、protected),效果相同。
- 运算符重载:友元常用于运算符重载,特别是需要访问私有成员的运算符。
友元在运算符重载中的应用
cpp
#include <iostream>
using namespace std;
class Complex {
private:
double real;
double imag;
public:
Complex(double r = 0, double i = 0) : real(r), imag(i) {}
// 声明友元运算符函数
friend Complex operator+(const Complex& c1, const Complex& c2);
friend ostream& operator<<(ostream& os, const Complex& c);
};
// 友元运算符函数定义
Complex operator+(const Complex& c1, const Complex& c2) {
return Complex(c1.real + c2.real, c1.imag + c2.imag);
}
ostream& operator<<(ostream& os, const Complex& c) {
os << c.real << " + " << c.imag << "i";
return os;
}
int main() {
Complex c1(3.0, 4.0);
Complex c2(1.0, 2.0);
Complex c3 = c1 + c2;
cout << c3 << endl; // 输出: 4 + 6i
return 0;
}
总结
友元是 C++ 中一种特殊的访问控制机制,它提供了对类私有成员的有限访问权限。虽然友元破坏了封装性,但在某些场景下(如运算符重载、紧密协作的类之间)可以提高代码的灵活性和效率。使用时需要权衡封装性和便利性,避免过度使用导致代码耦合度过高。
五、内部类
内部类的概念
内部类(Inner Class)是定义在另一个类内部的类。它是一个独立的类,有自己的作用域和访问权限。
内部类的特点
- 内部类可以直接访问外部类的静态成员,无需外部类对象。
- 外部类不能直接访问内部类的私有成员,需要创建内部类对象。
- 内部类可以定义在 public、protected、private 区域,影响外部访问权限。
- 用 static 修饰的内部类称为静态内部类,不能访问外部类的非静态成员。
简单示例
cpp
#include <iostream>
using namespace std;
class Outer {
private:
static int count;
public:
class Inner {
public:
void show() {
cout << "Outer::count = " << Outer::count << endl;
}
};
};
int Outer::count = 10;
int main() {
Outer::Inner obj;
obj.show(); // 输出: Outer::count = 10
return 0;
}
主要用途
- 封装只在类内部使用的辅助类。
- 实现迭代器模式。
- 避免全局命名冲突。
六、匿名对象
匿名对象的概念
匿名对象是没有名字的临时对象,创建后立即使用,表达式结束即销毁。
匿名对象的特点
- 生命周期仅限于创建它的表达式。
- 无需变量名,创建后立即使用。
- 是右值,不能取地址(const 引用除外)。
基本用法
cpp
#include <iostream>
using namespace std;
class Test {
public:
Test(int v) { cout << "构造:" << v << endl; }
~Test() { cout << "析构" << endl; }
int getValue() { return 100; }
};
int main() {
// 创建匿名对象并调用成员函数
Test(10).getValue();
// 作为函数参数
int sum = Test(1).getValue() + Test(2).getValue();
// const 引用延长生命周期
const Test& ref = Test(30);
return 0;
}
常见场景
- 函数返回临时对象。
- 链式调用。
- 临时计算。
注意事项
- 生命周期短,不能保存指针(const 引用除外)。
- 频繁创建销毁可能影响性能。
- 调试时难以跟踪。