C++文章目录中每个主题的详解,内容简洁、清晰,涵盖核心概念,并尽量避免冗长叙述。如果需要更详细的代码示例或进一步解释某个部分,请告诉我!


一. 类和对象

1.1 封装

1.1.1 封装的意义

封装是面向对象编程的核心特性之一,通过将数据(成员变量)和操作数据的方法(成员函数)捆绑在一起,并控制访问权限,隐藏对象的内部实现细节。

  • 意义
    • 数据保护:防止外部直接访问和修改对象内部数据,保证数据安全。
    • 接口简化:提供公共接口(如get/set方法),隐藏复杂实现,提高代码可维护性。
    • 模块化:将功能封装成独立单元,便于代码复用和修改。
1.1.2 struct和class区别

在C++中,structclass都可以定义类,主要区别如下:

  • 默认访问权限
    • struct:成员默认是public
    • class:成员默认是private
  • 使用场景
    • struct:常用于数据结构,强调数据聚合,较少包含复杂逻辑。
    • class:常用于面向对象设计,包含数据和行为,强调封装。
  • 其他:除默认访问权限外,二者在功能上几乎无差别,struct支持继承、多态等。
1.1.3 成员属性设置为私有

将成员属性设为private是封装的重要实践:

  • 原因
    • 防止外部直接修改成员变量,保护数据完整性。
    • 通过public的get/set方法控制访问,增加灵活性(如验证输入)。
  • 实现
    class Person {
    private:
        string name;
    public:
        void setName(string n) { if (n != "") name = n; }
        string getName() { return name; }
    };
    

1.2 对象的初始化和清理

1.2.1 构造函数和析构函数
  • 构造函数
    • 与类同名,无返回值,对象创建时自动调用,用于初始化成员变量。
    • 可重载,支持多种初始化方式。
  • 析构函数
    • 名称为~类名,无参数无返回值,对象销毁时自动调用,用于释放资源(如动态内存)。
    class Person {
    public:
        Person() { cout << "Constructor" << endl; }
        ~Person() { cout << "Destructor" << endl; }
    };
    
1.2.2 构造函数的分类及调用
  • 分类
    • 无参构造函数Person() {},默认初始化。
    • 有参构造函数Person(string n) { name = n; },带参数初始化。
    • 拷贝构造函数Person(const Person& p) { name = p.name; },通过已有对象初始化新对象。
  • 调用方式
    • 括号法Person p("Alice");
    • 显示法Person p = Person("Alice");
    • 隐式转换法Person p = "Alice";(需有单参数构造函数)
    • 拷贝构造Person p2 = p1;
1.2.3 拷贝构造函数调用时机

拷贝构造函数在以下场景自动调用:

  • 用已有对象初始化新对象:Person p2 = p1;
  • 对象作为函数参数按值传递:void func(Person p) {}
  • 对象作为函数返回值按值返回:Person func() { return Person(); }
1.2.4 构造函数调用规则
  • 系统默认提供:无参构造函数、拷贝构造函数、析构函数。
  • 如果定义了有参构造函数,系统不再提供默认无参构造函数,但仍提供默认拷贝构造函数。
  • 如果定义了拷贝构造函数,系统不再提供其他构造函数。
1.2.5 深拷贝与浅拷贝
  • 浅拷贝:默认拷贝构造函数只拷贝值(包括指针地址),可能导致多个对象共享同一块动态内存,析构时重复释放引发错误。
  • 深拷贝:为指针成员重新分配内存并复制数据,避免浅拷贝问题。
    class Person {
    private:
        int* age;
    public:
        Person(int a) { age = new int(a); }
        Person(const Person& p) { age = new int(*p.age); } // 深拷贝
        ~Person() { delete age; }
    };
    
1.2.6 初始化列表

用于在构造函数中初始化成员变量,效率高于在构造函数体内赋值。

  • 语法
    class Person {
    private:
        string name;
        int age;
    public:
        Person(string n, int a) : name(n), age(a) {}
    };
    
  • 用途
    • 初始化const成员或引用成员。
    • 初始化无默认构造函数的类成员。
    • 提高初始化效率。
1.2.7 类对象作为类成员

类对象可以作为另一个类的成员,构造和析构顺序如下:

  • 构造顺序:先构造成员对象,再构造本类。
  • 析构顺序:先析构本类,再析构成员对象(与构造顺序相反)。
    class Phone {
    public:
        Phone() { cout << "Phone Constructor" << endl; }
        ~Phone() { cout << "Phone Destructor" << endl; }
    };
    class Person {
        Phone phone;
    public:
        Person() { cout << "Person Constructor" << endl; }
        ~Person() { cout << "Person Destructor" << endl; }
    };
    
1.2.8 静态成员
  • 静态成员变量:用static修饰,属于类而非对象,所有对象共享同一份数据,在程序开始时分配内存。
    class Person {
    public:
        static int count; // 声明
    };
    int Person::count = 0; // 定义并初始化
    
  • 静态成员函数:用static修饰,只能访问静态成员,不能访问非静态成员,无this指针。
    static void func() { cout << count << endl; }
    

1.3 C++对象模型和this指针

1.3.1 成员变量和成员函数分开存储
  • 对象模型
    • 成员变量存储在对象中,每个对象有自己的副本。
    • 成员函数存储在代码段,所有对象共享。
  • 内存布局:对象只存储成员变量,函数地址由编译器管理。
1.3.2 this指针概念
  • this是指向当前对象的指针,隐式存在于非静态成员函数中,用于区分对象实例。
  • 用途
    • 解决成员变量与参数名冲突:this->name = name;
    • 返回对象自身:return *this;
1.3.3 空指针访问成员函数
  • 空指针(如Person* p = nullptr)调用非静态成员函数可能导致崩溃,除非函数不访问成员变量。
    class Person {
    public:
        void func() { cout << "No member access" << endl; } // 安全
        void func2() { cout << name << endl; } // 崩溃
    private:
        string name;
    };
    Person* p = nullptr;
    p->func(); // 正常
    p->func2(); // 崩溃
    
1.3.4 const修饰成员函数
  • const成员函数(void func() const)承诺不修改成员变量。
  • 只能调用其他const成员函数,适用于只读操作。
    class Person {
    private:
        string name;
    public:
        void show() const { cout << name << endl; }
    };
    

1.4 友元

友元允许外部函数或类访问类的私有成员,打破封装但提供灵活性。

1.4.1 全局函数做友元
  • 声明全局函数为友元,允许其访问类的私有成员。
    class Person {
        friend void print(Person& p);
    private:
        string name;
    };
    void print(Person& p) { cout << p.name << endl; }
    
1.4.2 类做友元
  • 声明一个类为友元,允许其所有成员函数访问私有成员。
    class Friend;
    class Person {
        friend class Friend;
    private:
        string name;
    };
    class Friend {
    public:
        void visit(Person& p) { cout << p.name << endl; }
    };
    
1.4.3 成员函数做友元
  • 声明某个类的成员函数为友元,仅该函数可访问私有成员。
    class Friend;
    class Person {
        friend void Friend::visit(Person&);
    private:
        string name;
    };
    class Friend {
    public:
        void visit(Person& p) { cout << p.name << endl; }
    };
    

1.5 运算符重载

运算符重载允许自定义运算符的行为,使类对象支持类似内置类型的操作。

1.5.1 加号运算符重载
  • 重载+运算符,返回新对象。
    class Person {
    public:
        int age;
        Person operator+(const Person& p) {
            Person temp;
            temp.age = this->age + p.age;
            return temp;
        }
    };
    
1.5.2 左移运算符重载
  • 重载<<以支持cout输出,通常声明为全局友元函数。
    class Person {
        friend ostream& operator<<(ostream& out, const Person& p);
    private:
        string name;
    };
    ostream& operator<<(ostream& out, const Person& p) {
        out << p.name;
        return out;
    }
    
1.5.3 递增运算符重载
  • 前置++++obj,返回修改后的对象。
  • 后置++obj++,返回修改前的对象。
    class Person {
    public:
        int age;
        Person& operator++() { age++; return *this; } // 前置
        Person operator++(int) { Person temp = *this; age++; return temp; } // 后置
    };
    
1.5.4 赋值运算符重载
  • 重载=以支持深拷贝。
    class Person {
    private:
        int* age;
    public:
        Person& operator=(const Person& p) {
            if (age) delete age;
            age = new int(*p.age);
            return *this;
        }
    };
    
1.5.5 关系运算符重载
  • 重载==!=等,返回布尔值。
    class Person {
    public:
        int age;
        bool operator==(const Person& p) { return age == p.age; }
    };
    
1.5.6 函数调用运算符重载
  • 重载(),使对象像函数一样调用,称为“仿函数”。
    class MyFunc {
    public:
        int operator()(int a, int b) { return a + b; }
    };
    MyFunc f;
    cout << f(1, 2); // 输出3
    

1.6 继承

1.6.1 继承的基本语法

继承允许子类复用父类的成员,减少代码冗余。

  • 语法
    class Base {
    public:
        int x;
    };
    class Derived : public Base {
    public:
        int y;
    };
    
1.6.2 继承方式
  • public继承:父类public成员保持publicprotected保持protected
  • protected继承:父类publicprotected成员变为子类的protected
  • private继承:父类publicprotected成员变为子类的private
1.6.3 继承中的对象模型
  • 子类对象包含父类成员和自身成员,内存布局按声明顺序排列。
  • 父类的私有成员被继承但不可直接访问。
1.6.4 继承中构造和析构顺序
  • 构造:先调用父类构造函数,再调用子类构造函数。
  • 析构:先调用子类析构函数,再调用父类析构函数。
    class Base { ... };
    class Derived : public Base { ... };
    
1.6.5 继承同名成员处理方式
  • 子类访问父类同名成员需加作用域:Base::member
  • 如果子类重定义同名成员,父类成员被隐藏。
1.6.6 继承同名静态成员处理方式
  • 静态成员通过类名或对象访问:Base::staticVarobj.staticVar
  • 子类同名静态成员隐藏父类成员,需用Base::访问。
1.6.7 多继承语法
  • 一个子类继承多个父类:class Derived : public Base1, public Base2 {};
  • 注意:多继承可能导致复杂性和歧义(如菱形继承)。
1.6.8 菱形继承
  • 问题:多个父类继承自同一基类,导致子类包含多份基类成员。
  • 解决:使用虚继承(virtual public Base),使子类共享一份基类成员。
    class Base { public: int x; };
    class B1 : virtual public Base {};
    class B2 : virtual public Base {};
    class Derived : public B1, public B2 {};
    

1.7 多态

1.7.1 多态的基本概念

多态允许不同类的对象对同一消息作出不同响应,分为静态多态(函数重载、模板)和动态多态(虚函数)。

  • 动态多态条件
    • 继承关系。
    • 父类函数声明为virtual
    • 子类重写父类虚函数。
    • 父类指针或引用调用虚函数。
1.7.2 多态案例一-计算器类

通过虚函数实现不同运算方式:

class Calculator {
public:
    virtual int calculate(int a, int b) { return 0; }
};
class Add : public Calculator {
public:
    int calculate(int a, int b) override { return a + b; }
};
1.7.3 纯虚函数和抽象类
  • 纯虚函数virtual void func() = 0; 无实现,子类必须重写。
  • 抽象类:包含纯虚函数的类,不能实例化,仅作为基类。
    class Shape {
    public:
        virtual void draw() = 0;
    };
    
1.7.4 多态案例二-制作饮品

通过多态实现不同饮品制作流程:

class Drink {
public:
    virtual void make() = 0;
};
class Coffee : public Drink {
public:
    void make() override { cout << "Make Coffee" << endl; }
};
1.7.5 虚析构和纯虚析构
  • 虚析构virtual ~Base();,确保通过父类指针删除子类对象时调用子类析构函数。
  • 纯虚析构virtual ~Base() = 0;,需提供实现,防止内存泄漏。
    class Base { public: virtual ~Base() = 0; };
    Base::~Base() {}
    
1.7.6 多态案例三-电脑组装

通过多态实现不同品牌电脑的组装:

class Component {
public:
    virtual void install() = 0;
};
class CPU : public Component {
public:
    void install() override { cout << "Install CPU" << endl; }
};

以上是对目录中每个主题的简明讲解,涵盖了C++面向对象编程的核心概念。如果需要针对某个主题提供更详细的代码示例、进一步解释,或有其他问题,请告诉我!

更多推荐