1. C++类与对象的进阶探索
在C++编程中,类和对象是面向对象编程的核心概念。当我们掌握了基础的类定义和对象创建后,接下来需要深入理解类与对象在内存中的表现、特殊成员函数、运算符重载等高级特性。这些知识对于构建健壮、高效的C++程序至关重要。
提示:本文假设读者已经了解C++类的基本概念,包括成员变量、成员函数、访问控制等基础知识。如果对这些概念还不熟悉,建议先学习C++类和对象的基础部分。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 类的特殊成员函数详解
2.1 构造函数与析构函数进阶
构造函数在对象创建时自动调用,而析构函数在对象销毁时自动调用。但它们的应用远不止简单的初始化和清理。
cpp复制class MyString {
public:
// 默认构造函数
MyString() : data(nullptr), length(0) {}
// 参数化构造函数
MyString(const char* str) {
length = strlen(str);
data = new char[length + 1];
strcpy(data, str);
}
// 析构函数
~MyString() {
delete[] data;
}
private:
char* data;
size_t length;
};
在这个字符串类示例中,我们看到了构造函数如何分配内存,析构函数如何释放内存。这是RAII(Resource Acquisition Is Initialization)原则的典型应用。
2.2 拷贝控制成员:拷贝构造函数和拷贝赋值运算符
当类包含动态分配的资源时,默认的拷贝行为可能导致问题。这时需要自定义拷贝构造函数和拷贝赋值运算符。
cpp复制class MyString {
public:
// 拷贝构造函数
MyString(const MyString& other) {
length = other.length;
data = new char[length + 1];
strcpy(data, other.data);
}
// 拷贝赋值运算符
MyString& operator=(const MyString& other) {
if (this != &other) { // 防止自赋值
delete[] data; // 释放原有资源
length = other.length;
data = new char[length + 1];
strcpy(data, other.data);
}
return *this;
}
};
注意:拷贝赋值运算符通常需要处理自赋值情况,并遵循"拷贝并交换"的惯用法来保证异常安全。
2.3 移动语义:移动构造函数和移动赋值运算符
C++11引入了移动语义,可以避免不必要的拷贝,提高性能。
cpp复制class MyString {
public:
// 移动构造函数
MyString(MyString&& other) noexcept
: data(other.data), length(other.length) {
other.data = nullptr;
other.length = 0;
}
// 移动赋值运算符
MyString& operator=(MyString&& other) noexcept {
if (this != &other) {
delete[] data;
data = other.data;
length = other.length;
other.data = nullptr;
other.length = 0;
}
return *this;
}
};
移动操作通过"窃取"资源而非拷贝资源来提高效率,特别适合处理大型对象或资源密集型对象。
3. 运算符重载的艺术
3.1 基本运算符重载
运算符重载允许我们为自定义类型定义运算符的行为。例如,为我们的MyString类重载+运算符实现字符串拼接:
cpp复制MyString operator+(const MyString& lhs, const MyString& rhs) {
MyString result;
result.length = lhs.length + rhs.length;
result.data = new char[result.length + 1];
strcpy(result.data, lhs.data);
strcat(result.data, rhs.data);
return result;
}
3.2 输入输出运算符重载
重载<<和>>运算符可以使我们的类与标准I/O流无缝协作:
cpp复制std::ostream& operator<<(std::ostream& os, const MyString& str) {
if (str.data) os << str.data;
return os;
}
std::istream& operator>>(std::istream& is, MyString& str) {
char buffer[1024];
is >> buffer;
str = MyString(buffer); // 假设我们已经定义了相应的构造函数
return is;
}
3.3 下标运算符和函数调用运算符
下标运算符[]和函数调用运算符()也可以被重载:
cpp复制class MyString {
public:
char& operator[](size_t index) {
if (index >= length) throw std::out_of_range("Index out of range");
return data[index];
}
const char& operator[](size_t index) const {
if (index >= length) throw std::out_of_range("Index out of range");
return data[index];
}
// 函数调用运算符示例
MyString operator()(size_t start, size_t count) const {
if (start + count > length) throw std::out_of_range("Invalid substring range");
MyString substr;
substr.length = count;
substr.data = new char[count + 1];
strncpy(substr.data, data + start, count);
substr.data[count] = '\0';
return substr;
}
};
4. 类的静态成员与友元
4.1 静态成员变量和函数
静态成员属于类本身而非类的对象,它们在所有对象间共享:
cpp复制class Employee {
private:
static int count; // 静态成员变量声明
std::string name;
public:
Employee(const std::string& n) : name(n) { ++count; }
~Employee() { --count; }
static int getCount() { return count; } // 静态成员函数
};
int Employee::count = 0; // 静态成员变量定义和初始化
静态成员函数只能访问静态成员变量,不能访问非静态成员。
4.2 友元函数和友元类
友元机制允许特定函数或类访问当前类的私有成员:
cpp复制class Matrix {
private:
int data[4][4];
public:
friend Matrix operator*(const Matrix&, const Matrix&); // 友元函数
friend class MatrixPrinter; // 友元类
};
Matrix operator*(const Matrix& a, const Matrix& b) {
Matrix result;
// 可以直接访问Matrix的私有成员data
for (int i = 0; i < 4; ++i) {
for (int j = 0; j < 4; ++j) {
result.data[i][j] = 0;
for (int k = 0; k < 4; ++k) {
result.data[i][j] += a.data[i][k] * b.data[k][j];
}
}
}
return result;
}
class MatrixPrinter {
public:
void print(const Matrix& m) {
for (int i = 0; i < 4; ++i) {
for (int j = 0; j < 4; ++j) {
std::cout << m.data[i][j] << " "; // 可以直接访问Matrix的私有成员
}
std::cout << std::endl;
}
}
};
5. 类的高级特性与应用
5.1 嵌套类和局部类
类可以定义在其他类内部,形成嵌套类:
cpp复制class LinkedList {
private:
class Node { // 嵌套类
public:
int data;
Node* next;
Node(int d) : data(d), next(nullptr) {}
};
Node* head;
public:
LinkedList() : head(nullptr) {}
void append(int data) {
Node* newNode = new Node(data);
if (!head) {
head = newNode;
} else {
Node* temp = head;
while (temp->next) temp = temp->next;
temp->next = newNode;
}
}
};
局部类则定义在函数内部,使用场景较少但有时很有用。
5.2 类的前向声明
当类之间相互引用时,可以使用前向声明:
cpp复制class B; // 前向声明
class A {
public:
void doSomething(B& b);
};
class B {
public:
void doSomethingElse(A& a);
};
// 成员函数定义可以放在两个类都定义完之后
void A::doSomething(B& b) { /* ... */ }
void B::doSomethingElse(A& a) { /* ... */ }
5.3 类与const的正确使用
const正确性对于编写健壮的C++代码非常重要:
cpp复制class Rational {
public:
Rational(int num = 0, int denom = 1) : numerator(num), denominator(denom) {}
// const成员函数,承诺不修改对象状态
double toDouble() const {
return static_cast<double>(numerator) / denominator;
}
// 非const成员函数,可以修改对象状态
void setNumerator(int num) { numerator = num; }
private:
int numerator;
int denominator;
};
void printRational(const Rational& r) {
// 只能调用const成员函数
std::cout << r.toDouble() << std::endl;
// r.setNumerator(5); // 错误!不能在const对象上调用非const成员函数
}
6. 类的设计原则与最佳实践
6.1 单一职责原则
一个类应该只有一个引起它变化的原因。这意味着一个类应该只负责一项职责。
cpp复制// 不好的设计:FileProcessor类负责太多事情
class FileProcessor {
public:
void readFile(const std::string& filename);
void processData();
void saveToDatabase();
void generateReport();
};
// 好的设计:拆分职责
class FileReader { /* ... */ };
class DataProcessor { /* ... */ };
class DatabaseSaver { /* ... */ };
class ReportGenerator { /* ... */ };
6.2 开放封闭原则
类应该对扩展开放,对修改封闭。这意味着应该通过添加新代码来扩展功能,而不是修改现有代码。
cpp复制// 基础形状类
class Shape {
public:
virtual double area() const = 0;
virtual ~Shape() = default;
};
// 具体形状类
class Circle : public Shape {
public:
Circle(double r) : radius(r) {}
double area() const override { return 3.14159 * radius * radius; }
private:
double radius;
};
class Rectangle : public Shape {
public:
Rectangle(double w, double h) : width(w), height(h) {}
double area() const override { return width * height; }
private:
double width, height;
};
// 可以轻松添加新的形状类而不需要修改现有代码
class Triangle : public Shape {
public:
Triangle(double b, double h) : base(b), height(h) {}
double area() const override { return 0.5 * base * height; }
private:
double base, height;
};
6.3 依赖倒置原则
高层模块不应该依赖低层模块,两者都应该依赖抽象。抽象不应该依赖细节,细节应该依赖抽象。
cpp复制// 抽象接口
class Logger {
public:
virtual void log(const std::string& message) = 0;
virtual ~Logger() = default;
};
// 具体实现
class FileLogger : public Logger {
public:
void log(const std::string& message) override {
// 实现文件日志记录
}
};
class ConsoleLogger : public Logger {
public:
void log(const std::string& message) override {
// 实现控制台日志记录
}
};
// 高层模块依赖抽象接口
class Application {
public:
Application(Logger& logger) : logger(logger) {}
void doSomething() {
logger.log("Doing something...");
}
private:
Logger& logger;
};
7. 类与对象的内存模型
7.1 对象的内存布局
理解对象在内存中的布局对于编写高效C++代码很重要。考虑以下类:
cpp复制class Example {
public:
Example() : a(0), b(0.0), c(false) {}
private:
int a;
double b;
bool c;
};
在大多数实现中,这个类的对象在内存中大致如下布局:
code复制+---------+---------+---------+
| int | double | bool |
| a | b | c |
+---------+---------+---------+
然而,由于对齐要求,编译器可能会在成员之间插入填充字节。使用sizeof运算符可以查看类的总大小,使用offsetof宏可以查看成员的偏移量。
7.2 虚函数表与多态
当类包含虚函数时,编译器会为其生成虚函数表(vtable),每个对象包含一个指向vtable的指针:
cpp复制class Base {
public:
virtual void func1() {}
virtual void func2() {}
virtual ~Base() {}
};
class Derived : public Base {
public:
void func1() override {}
void func3() {}
};
Derived类的vtable大致如下:
code复制Derived vtable:
+---------------------+
| &Derived::func1 |
| &Base::func2 |
| &Derived::~Derived |
| &Derived::func3 |
+---------------------+
每个Derived对象包含一个指向这个vtable的指针,这是实现运行时多态的基础。
7.3 对象切片问题
当派生类对象被赋值给基类对象时,会发生对象切片,丢失派生类特有的部分:
cpp复制class Base {
public:
int base_data;
};
class Derived : public Base {
public:
int derived_data;
};
Derived d;
Base b = d; // 对象切片,只复制了Base部分
为了避免切片,应该使用指针或引用:
cpp复制Base* pb = new Derived(); // 正确,无切片
Base& rb = d; // 正确,无切片
8. 类模板与模板类
8.1 类模板基础
类模板允许我们编写与类型无关的代码:
cpp复制template <typename T>
class Stack {
public:
Stack() : top(-1), capacity(10) {
data = new T[capacity];
}
~Stack() {
delete[] data;
}
void push(const T& item) {
if (top == capacity - 1) expand();
data[++top] = item;
}
T pop() {
if (empty()) throw std::out_of_range("Stack underflow");
return data[top--];
}
bool empty() const { return top == -1; }
private:
T* data;
int top;
int capacity;
void expand() {
capacity *= 2;
T* newData = new T[capacity];
for (int i = 0; i <= top; ++i) {
newData[i] = data[i];
}
delete[] data;
data = newData;
}
};
8.2 模板特化与偏特化
我们可以为特定类型提供模板的特化版本:
cpp复制// 通用版本
template <typename T>
class Printer {
public:
void print(const T& value) {
std::cout << value << std::endl;
}
};
// 为char*特化
template <>
class Printer<char*> {
public:
void print(const char* value) {
std::cout << "C-string: " << value << std::endl;
}
};
// 偏特化:为指针类型提供特殊实现
template <typename T>
class Printer<T*> {
public:
void print(T* value) {
std::cout << "Pointer to " << *value << std::endl;
}
};
8.3 模板元编程基础
C++模板系统是图灵完备的,可以在编译期进行计算:
cpp复制template <int N>
struct Factorial {
static const int value = N * Factorial<N - 1>::value;
};
template <>
struct Factorial<0> {
static const int value = 1;
};
// 使用
int main() {
std::cout << Factorial<5>::value << std::endl; // 输出120
}
虽然现代C++更倾向于使用constexpr函数来实现编译期计算,但理解模板元编程对于深入掌握C++仍然很有价值。
9. 现代C++中的类特性
9.1 默认和删除的函数
C++11允许我们显式指定使用默认实现或删除函数:
cpp复制class NonCopyable {
public:
NonCopyable() = default;
NonCopyable(const NonCopyable&) = delete;
NonCopyable& operator=(const NonCopyable&) = delete;
};
class DefaultMove {
public:
DefaultMove() = default;
DefaultMove(DefaultMove&&) = default;
DefaultMove& operator=(DefaultMove&&) = default;
~DefaultMove() = default;
};
9.2 override和final说明符
override确保我们确实重写了基类的虚函数,final防止派生类进一步重写:
cpp复制class Base {
public:
virtual void func() {}
virtual void finalFunc() final {}
};
class Derived : public Base {
public:
void func() override {} // 正确,重写基类虚函数
// void finalFunc() override {} // 错误,基类已标记为final
};
class FinalDerived final : public Derived {
// void func() override {} // 允许,但FinalDerived不能被继承
};
9.3 委托构造函数和继承构造函数
C++11引入了委托构造函数,允许一个构造函数调用同类中的另一个构造函数:
cpp复制class MyClass {
public:
MyClass(int x) : x(x) {}
MyClass() : MyClass(0) {} // 委托给MyClass(int)
private:
int x;
};
C++11还引入了继承构造函数,允许派生类继承基类的构造函数:
cpp复制class Base {
public:
Base(int);
Base(double);
};
class Derived : public Base {
public:
using Base::Base; // 继承Base的构造函数
};
10. 类设计中的常见陷阱与解决方案
10.1 虚析构函数问题
当基类指针指向派生类对象时,如果基类析构函数不是虚函数,会导致派生类部分不被正确销毁:
cpp复制class Base {
public:
~Base() { std::cout << "Base destructor" << std::endl; }
};
class Derived : public Base {
public:
~Derived() { std::cout << "Derived destructor" << std::endl; }
};
int main() {
Base* p = new Derived();
delete p; // 只调用Base的析构函数,内存泄漏!
}
解决方案是将基类析构函数声明为虚函数:
cpp复制class Base {
public:
virtual ~Base() { std::cout << "Base destructor" << std::endl; }
};
10.2 菱形继承问题
多重继承可能导致菱形继承问题:
cpp复制class A { public: int data; };
class B : public A {};
class C : public A {};
class D : public B, public C {}; // 菱形继承
int main() {
D d;
// d.data = 10; // 错误,歧义:是通过B还是C继承的data?
d.B::data = 10; // 需要显式指定
d.C::data = 20; // 这实际上是两个不同的data成员
}
解决方案是使用虚继承:
cpp复制class A { public: int data; };
class B : virtual public A {};
class C : virtual public A {};
class D : public B, public C {};
int main() {
D d;
d.data = 10; // 现在只有一个data成员
}
10.3 异常安全问题
构造函数中的异常可能导致资源泄漏:
cpp复制class ResourceHolder {
public:
ResourceHolder() : res1(new Resource), res2(new Resource) {
// 如果res2分配失败,res1会泄漏
}
private:
Resource* res1;
Resource* res2;
};
解决方案是使用智能指针或"资源获取即初始化"(RAII)惯用法:
cpp复制class ResourceHolder {
public:
ResourceHolder()
: res1(std::make_unique<Resource>()),
res2(std::make_unique<Resource>()) {
// 如果res2分配失败,res1会自动释放
}
private:
std::unique_ptr<Resource> res1;
std::unique_ptr<Resource> res2;
};
11. 类与对象在实际项目中的应用
11.1 设计模式中的类应用
许多设计模式都依赖于类的特性。例如,单例模式:
cpp复制class Singleton {
public:
static Singleton& getInstance() {
static Singleton instance; // 线程安全的局部静态变量(C++11及以上)
return instance;
}
// 删除拷贝构造函数和赋值运算符
Singleton(const Singleton&) = delete;
Singleton& operator=(const Singleton&) = delete;
void doSomething() { /* ... */ }
private:
Singleton() = default; // 私有构造函数
};
11.2 策略模式实现
策略模式使用类的多态性来在运行时改变算法:
cpp复制class SortStrategy {
public:
virtual void sort(std::vector<int>& data) = 0;
virtual ~SortStrategy() = default;
};
class QuickSort : public SortStrategy {
public:
void sort(std::vector<int>& data) override {
// 实现快速排序
}
};
class MergeSort : public SortStrategy {
public:
void sort(std::vector<int>& data) override {
// 实现归并排序
}
};
class Sorter {
public:
void setStrategy(SortStrategy* strategy) {
this->strategy = strategy;
}
void sort(std::vector<int>& data) {
if (strategy) strategy->sort(data);
}
private:
SortStrategy* strategy = nullptr;
};
11.3 观察者模式实现
观察者模式使用类之间的松耦合关系:
cpp复制class Observer {
public:
virtual void update(const std::string& message) = 0;
virtual ~Observer() = default;
};
class Subject {
public:
void attach(Observer* observer) {
observers.push_back(observer);
}
void detach(Observer* observer) {
observers.erase(std::remove(observers.begin(), observers.end(), observer), observers.end());
}
void notify(const std::string& message) {
for (auto observer : observers) {
observer->update(message);
}
}
private:
std::vector<Observer*> observers;
};
class ConcreteObserver : public Observer {
public:
void update(const std::string& message) override {
std::cout << "Received: " << message << std::endl;
}
};
12. 性能考量与优化
12.1 对象创建与销毁开销
频繁创建和销毁对象可能影响性能。考虑使用对象池:
cpp复制template <typename T>
class ObjectPool {
public:
ObjectPool(size_t initialSize) {
for (size_t i = 0; i < initialSize; ++i) {
pool.push(new T());
}
}
~ObjectPool() {
while (!pool.empty()) {
delete pool.top();
pool.pop();
}
}
T* acquire() {
if (pool.empty()) {
return new T();
}
T* obj = pool.top();
pool.pop();
return obj;
}
void release(T* obj) {
pool.push(obj);
}
private:
std::stack<T*> pool;
};
12.2 内联函数与性能
将小型成员函数声明为内联可以减少函数调用开销:
cpp复制class Point {
public:
Point(int x, int y) : x(x), y(y) {}
// 隐式内联
int getX() const { return x; }
int getY() const { return y; }
// 显式内联
inline void setX(int newX) { x = newX; }
inline void setY(int newY) { y = newY; }
private:
int x, y;
};
12.3 避免不必要的拷贝
使用移动语义和完美转发可以减少不必要的拷贝:
cpp复制class DataHolder {
public:
// 接受左值或右值的构造函数
template <typename T>
DataHolder(T&& data) : data(std::forward<T>(data)) {}
private:
std::vector<int> data;
};
int main() {
std::vector<int> v{1, 2, 3};
DataHolder h1(v); // 拷贝构造
DataHolder h2(std::move(v)); // 移动构造
DataHolder h3({4, 5, 6}); // 直接构造
}
13. C++20中对类的新增特性
13.1 三向比较运算符
C++20引入了三向比较运算符(<=>),简化了比较运算符的实现:
cpp复制class Point {
public:
Point(int x, int y) : x(x), y(y) {}
auto operator<=>(const Point& other) const = default;
private:
int x, int y;
};
// 现在可以使用所有比较运算符:==, !=, <, <=, >, >=
13.2 概念(Concepts)与类模板
概念(Concepts)可以约束模板参数,使错误信息更友好:
cpp复制template <typename T>
concept Drawable = requires(T t) {
{ t.draw() } -> std::same_as<void>;
};
template <Drawable T>
class Renderer {
public:
void render(const T& obj) {
obj.draw();
}
};
class Circle {
public:
void draw() const { /* ... */ }
};
// Renderer<Circle> 可以编译
// Renderer<int> 会给出清晰的错误信息
13.3 协程支持
C++20引入了协程支持,可以用于实现生成器等模式:
cpp复制#include <coroutine>
class Generator {
public:
struct promise_type {
int current_value;
Generator get_return_object() {
return Generator{std::coroutine_handle<promise_type>::from_promise(*this)};
}
std::suspend_always initial_suspend() { return {}; }
std::suspend_always final_suspend() noexcept { return {}; }
void unhandled_exception() { std::terminate(); }
std::suspend_always yield_value(int value) {
current_value = value;
return {};
}
void return_void() {}
};
using handle_type = std::coroutine_handle<promise_type>;
explicit Generator(handle_type h) : handle(h) {}
~Generator() { if (handle) handle.destroy(); }
int value() const { return handle.promise().current_value; }
bool next() {
if (!handle.done()) {
handle.resume();
return !handle.done();
}
return false;
}
private:
handle_type handle;
};
Generator range(int start, int end) {
for (int i = start; i < end; ++i) {
co_yield i;
}
}
14. 跨平台开发中的类设计考虑
14.1 平台相关代码的封装
将平台相关代码封装在类中,提供统一的接口:
cpp复制class FileSystem {
public:
virtual ~FileSystem() = default;
virtual bool exists(const std::string& path) = 0;
virtual std::vector<std::string> listFiles(const std::string& path) = 0;
};
#ifdef _WIN32
class WindowsFileSystem : public FileSystem {
public:
bool exists(const std::string& path) override {
// Windows实现
}
std::vector<std::string> listFiles(const std::string& path) override {
// Windows实现
}
};
#else
class UnixFileSystem : public FileSystem {
public:
bool exists(const std::string& path) override {
// Unix实现
}
std::vector<std::string> listFiles(const std::string& path) override {
// Unix实现
}
};
#endif
std::unique_ptr<FileSystem> createFileSystem() {
#ifdef _WIN32
return std::make_unique<WindowsFileSystem>();
#else
return std::make_unique<UnixFileSystem>();
#endif
}
14.2 字节序处理
在网络编程中,需要考虑字节序问题:
cpp复制class NetworkBuffer {
public:
template <typename T>
static T ntoh(T value) {
if constexpr (sizeof(T) == 2) {
return ntohs(value);
} else if constexpr (sizeof(T) == 4) {
return ntohl(value);
} else {
static_assert(sizeof(T) == 2 || sizeof(T) == 4,
"Unsupported type size for byte order conversion");
}
}
template <typename T>
static T hton(T value) {
if constexpr (sizeof(T) == 2) {
return htons(value);
} else if constexpr (sizeof(T) == 4) {
return htonl(value);
} else {
static_assert(sizeof(T) == 2 || sizeof(T) == 4,
"Unsupported type size for byte order conversion");
}
}
};
14.3 线程安全类设计
设计线程安全的类需要考虑同步问题:
cpp复制class ThreadSafeQueue {
public:
void push(int value) {
std::lock_guard<std::mutex> lock(mutex);
queue.push(value);
cond.notify_one();
}
bool try_pop(int& value) {
std::lock_guard<std::mutex> lock(mutex);
if (queue.empty()) return false;
value = queue.front();
queue.pop();
return true;
}
void wait_and_pop(int& value) {
std::unique_lock<std::mutex> lock(mutex);
cond.wait(lock, [this] { return !queue.empty(); });
value = queue.front();
queue.pop();
}
private:
std::queue<int> queue;
std::mutex mutex;
std::condition_variable cond;
};
15. 测试与调试技巧
15.1 单元测试中的类测试
使用测试框架如Google Test测试类:
cpp复制#include <gtest/gtest.h>
class MyClassTest : public ::testing::Test {
protected:
void SetUp() override {
// 测试前设置
obj = std::make_unique<MyClass>();
}
void TearDown() override {
// 测试后清理
obj.reset();
}
std::unique_ptr<MyClass> obj;
};
TEST_F(MyClassTest, InitialState) {
EXPECT_EQ(obj->getValue(), 0);
}
TEST_F(MyClassTest, MethodBehavior) {
obj->doSomething();
EXPECT_TRUE(obj->isDone());
}
15.2 调试技巧:打印对象状态
为类重载<<运算符便于调试:
cpp复制class Complex {
public:
Complex(double r, double i) : real(r), imag(i) {}
friend std::ostream& operator<<(std::ostream& os, const Complex& c) {
os << "(" << c.real << " + " << c.imag << "i)";
return os;
}
private:
double real, imag;
};
// 调试时可以方便地打印对象
Complex c(1.0, 2.0);
std::cout << "Complex number: " << c << std::endl;
15.3 使用typeid和dynamic_cast进行运行时类型检查
在需要时可以进行运行时类型检查:
cpp复制class Base {
public:
virtual ~Base() = default;
};
class Derived : public Base {
public:
void specificMethod() {}
};
void process(Base* obj) {
if (typeid(*obj) == typeid(Derived)) {
Derived* d = dynamic_cast<Derived*>(obj);
if (d) d->specificMethod();
}
// 或者使用dynamic_cast直接尝试转换
if (Derived* d = dynamic_cast<Derived*>(obj)) {
d->specificMethod();
}
}
16. 类与对象的最佳实践总结
16.1 资源管理原则
遵循RAII原则管理资源:
cpp复制class FileHandle {
public:
explicit FileHandle(const std::string& filename, const std::string& mode)
: file(fopen(filename.c_str(), mode.c_str())) {
if (!file) throw std::runtime_error("Failed to open file");
}
~FileHandle() {
if (file) fclose(file);
}
// 删除拷贝操作
FileHandle(const FileHandle&) = delete;
FileHandle& operator=(const FileHandle&) = delete;
// 允许移动操作
FileHandle(FileHandle&& other) noexcept : file(other.file) {
other.file = nullptr;
}
FileHandle& operator=(FileHandle&& other) noexcept {
if (this != &other) {
if (file) fclose(file);
file = other.file;
other.file = nullptr;
}
return *this;
}
FILE* get() const { return file; }
private:
FILE* file;
};
16.2 接口设计原则
设计清晰、简洁的接口:
cpp复制// 不好的设计:接口过于复杂
class ComplexInterface {
public:
void setAllParameters(int p1, double p2, const std::string& p3, /*...*/);
// ...
};
// 好的设计:使用构建器模式简化接口
class Config {
public:
class Builder {
public:
Builder& setParam1(int value) { param1 = value; return *this; }
Builder& setParam2(double value) { param2 = value; return *this; }
// ...
Config build() { return Config(param1, param2, /*...*/); }
private:
int param1;
double param2;
// ...
};
private:
Config(int p1, double p2, /*...*/) : /*...*/ {}
int param1;
double param2;
// ...
};
// 使用
Config config = Config::Builder()
.setParam1(42)
.setParam2(3.14)
.build();
16.3 性能与可读性平衡
在保持代码可读性的同时考虑性能:
cpp复制// 可读性优先的版本
class Vector {
public:
Vector operator+(const Vector& other) const {
Vector result;
result.x = x + other.x;
result.y = y + other.y;
result.z = z + other.z;
return result;
}
private:
double x, y, z;
};
// 性能优化的版本(可能影响可读性)
class Vector {
public:
Vector operator+(const Vector& other) const {
return Vector(x + other.x, y + other.y, z + other.z);
}
private:
Vector(double x, double y, double z) : x(x), y(y), z(z) {}
double x, y, z;
};
在实际项目中,应该根据性能需求和使用场景选择合适的实现方式。对于性能关键代码,可以在保证正确性的前提下进行优化;对于一般代码,可读性和可维护性更为重要。
