1. C++ 类与对象深度解析
在C++编程中,类和对象是最基础也是最核心的概念。很多初学者在掌握了基本语法后,往往对更深层次的应用感到困惑。今天我们就来深入探讨类和对象在实际开发中的高级用法和常见问题。
1.1 类的基本结构回顾
一个完整的类定义通常包含以下几个部分:
cpp复制class MyClass {
private:
// 私有成员变量和函数
int privateVar;
void privateFunc();
public:
// 公有成员变量和函数
int publicVar;
void publicFunc();
protected:
// 保护成员变量和函数
int protectedVar;
void protectedFunc();
// 构造函数和析构函数
MyClass();
~MyClass();
};
注意:在实际开发中,应该尽量减少公有成员变量的使用,而是通过公有成员函数来访问和修改私有成员变量,这符合面向对象编程的封装原则。
1.2 构造函数和析构函数的高级用法
构造函数和析构函数是类中非常重要的特殊成员函数,它们分别在对象创建和销毁时自动调用。
1.2.1 构造函数重载
我们可以为一个类定义多个构造函数,以适应不同的初始化需求:
cpp复制class Person {
private:
string name;
int age;
public:
// 默认构造函数
Person() : name("Unknown"), age(0) {}
// 带参数的构造函数
Person(string n) : name(n), age(0) {}
// 带两个参数的构造函数
Person(string n, int a) : name(n), age(a) {}
// 拷贝构造函数
Person(const Person &other) : name(other.name), age(other.age) {}
};
1..2 初始化列表的使用
在构造函数中,使用初始化列表来初始化成员变量是推荐的做法,特别是对于const成员和引用成员:
cpp复制class Example {
private:
const int constValue;
int &refValue;
int normalValue;
public:
Example(int cv, int &rv) : constValue(cv), refValue(rv), normalValue(0) {
// 构造函数体
}
};
提示:初始化列表中的初始化顺序是由成员变量在类中声明的顺序决定的,而不是初始化列表中的顺序。
1.3 静态成员
静态成员是属于类本身的,而不是类的某个对象。静态成员在所有对象间共享。
cpp复制class Counter {
private:
static int count; // 静态成员变量声明
public:
Counter() { count++; }
~Counter() { count--; }
static int getCount() { return count; } // 静态成员函数
};
// 静态成员变量定义和初始化
int Counter::count = 0;
静态成员函数只能访问静态成员变量,不能访问非静态成员变量。
1.4 友元函数和友元类
友元机制打破了类的封装性,但在某些情况下可以提高效率。
cpp复制class A {
private:
int secret;
// 声明友元函数
friend void showSecret(A &a);
// 声明友元类
friend class B;
};
void showSecret(A &a) {
cout << a.secret; // 可以访问私有成员
}
class B {
public:
void peek(A &a) {
cout << a.secret; // 可以访问A的私有成员
}
};
警告:过度使用友元会破坏封装性,应该谨慎使用。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 类的继承与多态
2.1 继承的基本概念
继承是面向对象编程的三大特性之一,它允许我们基于已有的类创建新类。
cpp复制// 基类
class Shape {
protected:
int width, height;
public:
void setWidth(int w) { width = w; }
void setHeight(int h) { height = h; }
};
// 派生类
class Rectangle : public Shape {
public:
int getArea() { return width * height; }
};
继承方式有三种:public、protected和private,它们决定了基类成员在派生类中的访问权限。
2.2 多态与虚函数
多态允许我们通过基类指针或引用来调用派生类的函数。
cpp复制class Animal {
public:
virtual void makeSound() {
cout << "Animal sound" << endl;
}
};
class Dog : public Animal {
public:
void makeSound() override {
cout << "Woof!" << endl;
}
};
class Cat : public Animal {
public:
void makeSound() override {
cout << "Meow!" << endl;
}
};
void animalSound(Animal &animal) {
animal.makeSound(); // 多态调用
}
2.3 纯虚函数与抽象类
包含纯虚函数的类称为抽象类,不能实例化。
cpp复制class AbstractShape {
public:
virtual double area() = 0; // 纯虚函数
};
class Circle : public AbstractShape {
private:
double radius;
public:
Circle(double r) : radius(r) {}
double area() override {
return 3.14159 * radius * radius;
}
};
3. 运算符重载
运算符重载允许我们为自定义类型定义运算符的行为。
cpp复制class Complex {
private:
double real, imag;
public:
Complex(double r = 0, double i = 0) : real(r), imag(i) {}
// 重载+运算符
Complex operator+(const Complex &other) {
return Complex(real + other.real, imag + other.imag);
}
// 重载<<运算符(通常声明为友元)
friend ostream &operator<<(ostream &out, const Complex &c);
};
ostream &operator<<(ostream &out, const Complex &c) {
out << c.real << "+" << c.imag << "i";
return out;
}
4. 类的其他高级特性
4.1 移动语义与右值引用
C++11引入了移动语义,可以避免不必要的拷贝。
cpp复制class String {
private:
char *data;
size_t length;
public:
// 移动构造函数
String(String &&other) noexcept
: data(other.data), length(other.length) {
other.data = nullptr;
other.length = 0;
}
// 移动赋值运算符
String &operator=(String &&other) noexcept {
if (this != &other) {
delete[] data;
data = other.data;
length = other.length;
other.data = nullptr;
other.length = 0;
}
return *this;
}
};
4.2 智能指针与资源管理
使用智能指针可以自动管理资源,避免内存泄漏。
cpp复制#include <memory>
class Resource {
public:
Resource() { cout << "Resource acquired\n"; }
~Resource() { cout << "Resource released\n"; }
void use() { cout << "Using resource\n"; }
};
void function() {
std::unique_ptr<Resource> res(new Resource());
res->use();
// 不需要手动delete,unique_ptr会在离开作用域时自动释放资源
}
4.3 类型转换运算符
我们可以为类定义类型转换运算符,使类对象可以隐式转换为其他类型。
cpp复制class Number {
private:
int value;
public:
Number(int v) : value(v) {}
// 转换为int
operator int() const { return value; }
// 转换为string
operator string() const { return to_string(value); }
};
5. 常见问题与解决方案
5.1 对象切片问题
当派生类对象赋值给基类对象时,会发生对象切片,派生类特有的部分会被"切掉"。
cpp复制class Base {
public:
int baseData;
};
class Derived : public Base {
public:
int derivedData;
};
void problem() {
Derived d;
Base b = d; // 对象切片,derivedData丢失
}
解决方案:使用指针或引用。
5.2 虚析构函数问题
如果基类有虚函数,应该将析构函数也声明为虚函数,否则通过基类指针删除派生类对象时,派生类的析构函数不会被调用。
cpp复制class Base {
public:
virtual ~Base() {} // 虚析构函数
};
class Derived : public Base {
public:
~Derived() override {
// 清理派生类特有资源
}
};
5.3 多重继承的菱形问题
多重继承可能导致菱形继承问题,可以使用虚继承解决。
cpp复制class A {
public:
int data;
};
class B : virtual public A {};
class C : virtual public A {};
class D : public B, public C {
// 现在只有一个A的副本
};
6. 实际应用案例
6.1 实现一个简单的字符串类
cpp复制class MyString {
private:
char *str;
size_t length;
public:
// 构造函数
MyString(const char *s = "") {
length = strlen(s);
str = new char[length + 1];
strcpy(str, s);
}
// 拷贝构造函数
MyString(const MyString &other) {
length = other.length;
str = new char[length + 1];
strcpy(str, other.str);
}
// 移动构造函数
MyString(MyString &&other) noexcept
: str(other.str), length(other.length) {
other.str = nullptr;
other.length = 0;
}
// 析构函数
~MyString() {
delete[] str;
}
// 赋值运算符
MyString &operator=(const MyString &other) {
if (this != &other) {
delete[] str;
length = other.length;
str = new char[length + 1];
strcpy(str, other.str);
}
return *this;
}
// 移动赋值运算符
MyString &operator=(MyString &&other) noexcept {
if (this != &other) {
delete[] str;
str = other.str;
length = other.length;
other.str = nullptr;
other.length = 0;
}
return *this;
}
// 重载+运算符
MyString operator+(const MyString &other) const {
MyString result;
result.length = length + other.length;
result.str = new char[result.length + 1];
strcpy(result.str, str);
strcat(result.str, other.str);
return result;
}
// 重载[]运算符
char &operator[](size_t index) {
if (index >= length) throw out_of_range("Index out of range");
return str[index];
}
// 重载<<运算符
friend ostream &operator<<(ostream &os, const MyString &s) {
os << s.str;
return os;
}
size_t size() const { return length; }
const char *c_str() const { return str; }
};
6.2 实现一个简单的智能指针
cpp复制template <typename T>
class SmartPointer {
private:
T *ptr;
int *refCount;
void release() {
if (--(*refCount) == 0) {
delete ptr;
delete refCount;
}
}
public:
// 构造函数
explicit SmartPointer(T *p = nullptr) : ptr(p), refCount(new int(1)) {}
// 拷贝构造函数
SmartPointer(const SmartPointer<T> &other)
: ptr(other.ptr), refCount(other.refCount) {
++(*refCount);
}
// 析构函数
~SmartPointer() {
release();
}
// 赋值运算符
SmartPointer<T> &operator=(const SmartPointer<T> &other) {
if (this != &other) {
release();
ptr = other.ptr;
refCount = other.refCount;
++(*refCount);
}
return *this;
}
T &operator*() const { return *ptr; }
T *operator->() const { return ptr; }
int useCount() const { return *refCount; }
};
7. 性能优化建议
7.1 避免不必要的拷贝
使用引用传递对象而不是值传递:
cpp复制// 不好
void processObject(MyClass obj);
// 好
void processObject(const MyClass &obj);
对于不会修改的参数,使用const引用。
7.2 使用移动语义
对于临时对象或即将销毁的对象,使用移动语义可以避免不必要的拷贝:
cpp复制MyClass createObject() {
MyClass obj;
// 初始化obj
return obj; // 编译器会优化为移动操作
}
void useObject() {
MyClass obj = createObject(); // 使用移动构造函数
}
7.3 内联小函数
对于简单的成员函数,可以声明为内联:
cpp复制class Point {
private:
int x, y;
public:
int getX() const { return x; } // 隐式内联
inline int getY() const { return y; } // 显式内联
};
7.4 对象池技术
对于频繁创建和销毁的对象,可以使用对象池技术:
cpp复制template <typename T>
class ObjectPool {
private:
std::vector<std::unique_ptr<T>> pool;
public:
T *acquire() {
if (pool.empty()) {
return new T();
}
auto obj = std::move(pool.back());
pool.pop_back();
return obj.release();
}
void release(T *obj) {
pool.push_back(std::unique_ptr<T>(obj));
}
};
8. 设计模式中的类应用
8.1 单例模式
确保一个类只有一个实例,并提供一个全局访问点。
cpp复制class Singleton {
private:
static Singleton *instance;
// 私有构造函数防止外部实例化
Singleton() {}
public:
// 删除拷贝构造函数和赋值运算符
Singleton(const Singleton &) = delete;
Singleton &operator=(const Singleton &) = delete;
static Singleton *getInstance() {
if (instance == nullptr) {
instance = new Singleton();
}
return instance;
}
};
Singleton *Singleton::instance = nullptr;
8.2 工厂模式
定义一个创建对象的接口,但让子类决定实例化哪个类。
cpp复制class Product {
public:
virtual ~Product() {}
virtual void operation() = 0;
};
class ConcreteProductA : public Product {
public:
void operation() override {
cout << "Product A operation" << endl;
}
};
class ConcreteProductB : public Product {
public:
void operation() override {
cout << "Product B operation" << endl;
}
};
class Creator {
public:
virtual ~Creator() {}
virtual Product *createProduct() = 0;
};
class ConcreteCreatorA : public Creator {
public:
Product *createProduct() override {
return new ConcreteProductA();
}
};
class ConcreteCreatorB : public Creator {
public:
Product *createProduct() override {
return new ConcreteProductB();
}
};
8.3 观察者模式
定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。
cpp复制#include <vector>
#include <algorithm>
class Observer {
public:
virtual ~Observer() {}
virtual void update() = 0;
};
class Subject {
private:
std::vector<Observer*> observers;
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() {
for (auto observer : observers) {
observer->update();
}
}
};
class ConcreteObserver : public Observer {
public:
void update() override {
cout << "Observer notified" << endl;
}
};
9. 现代C++中的类特性
9.1 override和final关键字
C++11引入了override和final关键字,使代码更清晰和安全。
cpp复制class Base {
public:
virtual void func() {}
virtual void finalFunc() final {}
};
class Derived : public Base {
public:
void func() override {} // 明确表示重写基类虚函数
// void finalFunc() {} // 错误,不能重写final函数
};
class FinalClass final {}; // 不能被继承
// class DerivedFinal : public FinalClass {}; // 错误
9.2 默认和删除函数
可以显式指定使用默认实现或删除某些函数。
cpp复制class MyClass {
public:
MyClass() = default; // 使用编译器生成的默认构造函数
MyClass(const MyClass &) = delete; // 禁止拷贝
MyClass &operator=(const MyClass &) = delete; // 禁止赋值
};
9.3 constexpr构造函数
C++11允许构造函数声明为constexpr,使得对象可以在编译期构造。
cpp复制class Point {
private:
int x, y;
public:
constexpr Point(int x = 0, int y = 0) : x(x), y(y) {}
constexpr int getX() const { return x; }
constexpr int getY() const { return y; }
};
constexpr Point p(1, 2); // 编译期构造
10. 测试与调试技巧
10.1 单元测试框架
使用如Google Test等框架进行单元测试:
cpp复制#include <gtest/gtest.h>
class MyClassTest : public ::testing::Test {
protected:
MyClass *obj;
void SetUp() override {
obj = new MyClass();
}
void TearDown() override {
delete obj;
}
};
TEST_F(MyClassTest, Initialization) {
EXPECT_EQ(obj->getValue(), 0);
}
TEST_F(MyClassTest, MethodTest) {
obj->setValue(42);
EXPECT_EQ(obj->getValue(), 42);
}
10.2 调试技巧
使用断言检查类不变式:
cpp复制class InvariantClass {
private:
int value;
void checkInvariant() const {
assert(value >= 0 && value <= 100); // 类不变式
}
public:
void setValue(int v) {
value = v;
checkInvariant();
}
};
10.3 日志记录
在关键方法中添加日志记录:
cpp复制#include <iostream>
#include <fstream>
class Logger {
private:
static std::ofstream logFile;
public:
static void init(const std::string &filename) {
logFile.open(filename);
}
static void log(const std::string &message) {
if (logFile.is_open()) {
logFile << message << std::endl;
}
}
};
class LoggedClass {
public:
void importantMethod() {
Logger::log("Entering importantMethod");
// 方法实现
Logger::log("Exiting importantMethod");
}
};
在实际开发中,合理使用这些高级特性可以大大提高代码的质量和效率。掌握类和对象的深层次用法是成为C++高级开发者的必经之路。
