C++类和对象(上)https://blog.youkuaiyun.com/wz_290793/article/details/149486147?spm=1001.2014.3001.5501
C++类和对象(中)https://blog.youkuaiyun.com/wz_290793/article/details/149505547?spm=1001.2014.3001.5501
目录
C++类和对象(下):从初始化列表到编译器优化
一、构造函数初始化列表:更高效的成员初始化方式
在C++中,构造函数初始化成员变量有两种主要方式:函数体内赋值和初始化列表。初始化列表以冒号开头,后跟以逗号分隔的成员初始化列表,每个成员变量后跟括号中的初始值或表达式。
class Example {
public:
Example(int x, int y) : m_x(x), m_y(y) {} // 初始化列表
private:
int m_x;
int m_y;
};
初始化列表的核心规则:
- 引用成员、const成员和没有默认构造的类类型成员必须使用初始化列表
- 每个成员在初始化列表中只能出现一次
- C++11支持成员变量声明时给缺省值,作为初始化列表的备选
- 初始化顺序由成员声明顺序决定,而非初始化列表中的顺序
为什么优先使用初始化列表?
- 效率更高:直接初始化而非先默认构造再赋值
- 某些类型只能通过初始化列表初始化
- 保持代码一致性,减少错误
二、类型转换:显式与隐式的艺术
C++支持丰富的类型转换机制,但不当使用可能导致难以发现的错误。
隐式类型转换
class MyInt {
public:
MyInt(int i) : value(i) {} // 转换构造函数
operator int() { return value; } // 转换函数
private:
int value;
};
MyInt mi = 42; // 隐式转换:int → MyInt
int i = mi; // 隐式转换:MyInt → int
显式类型转换
使用explicit
关键字禁止隐式转换:
class SafeInt {
public:
explicit SafeInt(int i) : value(i) {}
explicit operator int() { return value; }
private:
int value;
};
// SafeInt si = 42; // 错误:不能隐式转换
SafeInt si(42); // 正确:显式构造
int j = static_cast<int>(si); // 必须显式转换
最佳实践:
- 对可能引起歧义的构造函数使用
explicit
- 优先使用C++风格的类型转换(static_cast等)
- 避免C风格的强制类型转换
三、static成员:类的共享资源
static成员属于类而非对象,是所有类实例共享的资源。
static成员变量
class Counter {
public:
Counter() { ++count; }
~Counter() { --count; }
static int getCount() { return count; }
private:
static int count; // 声明
};
int Counter::count = 0; // 定义并初始化
static成员函数
- 没有this指针
- 只能访问static成员
- 可通过类名或对象调用
应用场景:
- 类级别的计数器
- 共享资源配置
- 工具函数(不依赖对象状态)
四、友元:打破封装的特权
友元机制允许特定函数或类访问私有成员,是一把双刃剑。
友元函数
class Secret {
private:
int secret_code;
friend void reveal(const Secret&); // 友元声明
};
void reveal(const Secret& s) {
cout << s.secret_code; // 可以访问私有成员
}
友元类
class Box {
private:
int width;
friend class BoxFriend; // 友元类声明
};
class BoxFriend {
public:
static void peek(const Box& b) {
cout << b.width; // 可以访问私有成员
}
};
友元使用原则:
- 最小化友元关系
- 文档化友元的设计理由
- 考虑替代方案(如公有接口)
- 友元关系不可传递(A→B→C ≠ A→C)
五、内部类:类中的类
内部类是定义在另一个类内部的类,具有特殊访问权限。
class Outer {
private:
int outer_data;
public:
class Inner { // 默认是Outer的友元
public:
void accessOuter(Outer& o) {
cout << o.outer_data; // 可以访问私有成员
}
};
};
内部类特点:
- 默认是外部类的友元
- 受外部类访问控制影响
- 适合实现仅被外部类使用的组件
- 不占用外部类对象空间
六、匿名对象:即用即弃的临时工
匿名对象是没有名称的临时对象,生命周期仅限当前表达式。
class Temp {
public:
Temp() { cout << "Created"; }
~Temp() { cout << "Destroyed"; }
void work() { cout << "Working"; }
};
int main() {
Temp().work(); // 输出:Created Working Destroyed
// 匿名对象在此已销毁
}
应用场景:
- 临时测试对象功能
- 链式调用中间结果
- 简化只使用一次的对象创建
七、编译器优化:看不见的性能提升
现代编译器会对对象拷贝进行各种优化,减少不必要的开销。
常见的拷贝优化
- 返回值优化(RVO):消除返回值的临时对象
- 命名返回值优化(NRVO):消除命名返回值的拷贝
- 拷贝省略:消除临时对象的构造和析构
class Heavy {
public:
Heavy() { cout << "Construct"; }
Heavy(const Heavy&) { cout << "Copy"; }
};
Heavy create() {
return Heavy(); // 可能被优化为直接构造返回值
}
int main() {
Heavy h = create(); // 可能只输出"Construct"
}
优化控制:
-fno-elide-constructors
:禁用拷贝优化(调试用)- 不同编译器优化策略可能不同
八、综合实例:一个完整的类设计
#include <iostream>
#include <string>
class Student {
public:
// explicit防止隐式转换
explicit Student(const std::string& name)
: name_(name), id_(++total_students) {}
// 静态成员函数
static int getTotalStudents() { return total_students; }
// 友元函数
friend void printStudentDetails(const Student&);
// 内部类
class Score {
public:
Score(int math, int physics)
: math_(math), physics_(physics) {}
void print() const {
std::cout << "Math: " << math_
<< ", Physics: " << physics_;
}
private:
int math_;
int physics_;
};
// 添加成绩(使用匿名对象)
void addScore(int math, int physics) {
score_ = Score(math, physics); // 匿名Score对象
}
// 显示成绩
void showScore() const {
score_.print();
}
private:
std::string name_;
int id_;
Score score_{0, 0}; // 使用C++11的成员初始化
static int total_students; // 静态成员计数
};
int Student::total_students = 0; // 静态成员定义
// 友元函数实现
void printStudentDetails(const Student& s) {
std::cout << "Student: " << s.name_
<< " (ID: " << s.id_ << ")\n";
s.showScore();
}
int main() {
Student alice("Alice");
alice.addScore(90, 85);
printStudentDetails(alice);
std::cout << "\nTotal students: "
<< Student::getTotalStudents();
return 0;
}
九、总结与最佳实践
- 初始化列表:优先使用,特别是对于const、引用和没有默认构造的成员
- 类型安全:合理使用explicit防止意外的隐式转换
- static成员:用于类级别的数据和功能
- 友元机制:谨慎使用,避免过度破坏封装
- 内部类:组织相关代码,增强封装性
- 匿名对象:简化一次性使用的对象创建
- 信任编译器:但了解其优化行为有助于写出更高效的代码
通过掌握这些高级特性,你可以设计出更安全、更高效、更易维护的C++类,为构建复杂系统打下坚实基础。
当然,c++类与对象的征程才刚刚开始,但学习是循序渐进的过程,本栏目将c++类与对象剩下的内容归为进阶c++,等后续初阶段知识文章发布完善,再更新c++进阶内容。