C++三大特性:封装、继承、多态

一、封装

1.1 定义

封装是将数据(属性)和对数据的操作(方法)捆绑在一起,并隐藏内部实现细节,只暴露必要的接口给外部使用。

1.2 主要特点

  • 数据隐藏:将数据设为私有,只能通过公共方法访问

  • 接口暴露:提供公共方法来操作数据

  • 实现隔离:内部实现变化不影响外部使用

1.3 优点

  • 提高安全性:防止数据被意外修改

  • 易于维护:内部实现变化不影响外部代码

  • 模块化:代码组织更清晰

class BankAccount {
private:  // 数据隐藏
    double balance;
    string accountNumber;

public:   // 接口暴露
    BankAccount(string accNum, double initialBalance) {
        accountNumber = accNum;
        balance = initialBalance;
    }
    
    // 公共接口方法
    void deposit(double amount) {
        if (amount > 0) {
            balance += amount;
        }
    }
    
    bool withdraw(double amount) {
        if (amount > 0 && amount <= balance) {
            balance -= amount;
            return true;
        }
        return false;
    }
    
    double getBalance() const {
        return balance;  // 只读访问
    }
};

// 使用
BankAccount account("12345", 1000);
account.deposit(500);      // 通过接口操作
// account.balance = 10000; // 错误:不能直接访问私有数据

1.4 访问权限概述

访问修饰符类内部派生类类外部
private✅ 可访问❌ 不可访问❌ 不可访问
protected✅ 可访问✅ 可访问❌ 不可访问
public✅ 可访问✅ 可访问✅ 可访问

1.4.1  private(私有)

  • 最严格的访问控制

  • 只能在本类内部访问

  • 派生类和外部代码都不能直接访问

class Base {
private:
    int privateVar;    // 私有成员变量
    void privateFunc() { // 私有成员函数
        cout << "Private function" << endl;
    }

public:
    Base() : privateVar(10) {}
    
    void accessPrivate() {
        // 类内部可以访问私有成员
        privateVar = 20;      // ✅ 可访问
        privateFunc();        // ✅ 可访问
    }
};

class Derived : public Base {
public:
    void tryAccess() {
        // privateVar = 30;   // ❌ 错误:派生类不能访问基类私有成员
        // privateFunc();     // ❌ 错误:派生类不能访问基类私有成员
    }
};

int main() {
    Base obj;
    // obj.privateVar = 40;   // ❌ 错误:外部不能访问私有成员
    // obj.privateFunc();     // ❌ 错误:外部不能访问私有成员
    obj.accessPrivate();      // ✅ 可访问:通过公有方法间接访问
    
    return 0;
}

1.4.2  protected(保护)

  • 中等访问控制

  • 本类和派生类可以访问

  • 外部代码不能直接访问

class Base {
protected:
    int protectedVar;         // 保护成员变量
    void protectedFunc() {    // 保护成员函数
        cout << "Protected function" << endl;
    }

private:
    int privateVar;

public:
    Base() : protectedVar(10), privateVar(20) {}
    
    void show() {
        cout << "Protected: " << protectedVar << endl;  // ✅ 可访问
        protectedFunc();                                // ✅ 可访问
    }
};

class Derived : public Base {
public:
    void accessProtected() {
        protectedVar = 30;    // ✅ 可访问:派生类可以访问基类保护成员
        protectedFunc();      // ✅ 可访问:派生类可以访问基类保护成员
        
        // privateVar = 40;   // ❌ 错误:不能访问基类私有成员
    }
};

int main() {
    Base obj;
    // obj.protectedVar = 50; // ❌ 错误:外部不能访问保护成员
    // obj.protectedFunc();   // ❌ 错误:外部不能访问保护成员
    obj.show();               // ✅ 可访问:通过公有方法间接访问
    
    return 0;
}

1.4.3  public(公有)

  • 最宽松的访问控制

  • 本类、派生类、外部代码都可以访问

class Base {
public:
    int publicVar;           // 公有成员变量
    void publicFunc() {      // 公有成员函数
        cout << "Public function" << endl;
    }

protected:
    int protectedVar;

private:
    int privateVar;
};

class Derived : public Base {
public:
    void accessAll() {
        publicVar = 10;      // ✅ 可访问:派生类可以访问基类公有成员
        publicFunc();        // ✅ 可访问:派生类可以访问基类公有成员
        protectedVar = 20;   // ✅ 可访问:派生类可以访问基类保护成员
        // privateVar = 30;  // ❌ 错误:不能访问基类私有成员
    }
};

int main() {
    Base obj;
    obj.publicVar = 40;      // ✅ 可访问:外部可以访问公有成员
    obj.publicFunc();        // ✅ 可访问:外部可以访问公有成员
    
    return 0;
}

二、 继承 (Inheritance)

2.1 定义

继承允许一个新类(派生类)基于现有类(基类)来创建,继承基类的特性和行为,并可以添加新的特性或重写现有行为。

2.2 主要特点

  • 代码复用:重用基类的代码

  • 层次结构:建立类之间的"is-a"关系

  • 扩展性:在基类基础上添加新功能

2.3 继承类型:

决定父类属性在子类中的访问权限,默认为private继承(与struct的唯一区别,struct默认为public继承)

  • 公有继承class Derived : public Base;父类属性在子类中保持原有访问权限

  • 保护继承class Derived : protected Base;父类public权限的属性在子类中升级为protected

  • 私有继承class Derived : private Base;父类public、protected权限的属性在子类中升级为private

class Base {
public:
    int publicVar;
protected:
    int protectedVar;
private:
    int privateVar;
};

// 公有继承
class PublicDerived : public Base {
    // publicVar → public
    // protectedVar → protected  
    // privateVar → 不可访问
};

// 保护继承  
class ProtectedDerived : protected Base {
    // publicVar → protected
    // protectedVar → protected
    // privateVar → 不可访问
};

// 私有继承
class PrivateDerived : private Base {
    // publicVar → private
    // protectedVar → private
    // privateVar → 不可访问
};

三、多态 (Polymorphism)

3.1 定义

多态意为"多种形态",指同一操作作用于不同的对象,可以有不同的解释,产生不同的执行结果。

3.2 主要特点

  • 接口统一:通过基类接口操作不同派生类对象

  • 动态绑定:运行时确定调用哪个方法

  • 灵活性:易于扩展新类型

3.3 多态类型

3.3.1 编译时多态(静态多态)

  • 函数重载:同一函数名可以根据输入参数类型、输入参数数量、输入参数顺序定义不同的实现,编译器会根据输入自行选择哪种实现方法。

#include <iostream>
using namespace std;

class Calculator {
public:
    // 重载的add函数 - 根据参数类型和数量不同
    int add(int a, int b) {
        cout << "整数相加: ";
        return a + b;
    }
    
    double add(double a, double b) {
        cout << "浮点数相加: ";
        return a + b;
    }
    
    int add(int a, int b, int c) {
        cout << "三个整数相加: ";
        return a + b + c;
    }
    
    string add(string a, string b) {
        cout << "字符串连接: ";
        return a + b;
    }
};

int main() {
    Calculator calc;
    
    cout << calc.add(5, 3) << endl;           // 调用 int add(int, int)
    cout << calc.add(2.5, 3.7) << endl;       // 调用 double add(double, double)
    cout << calc.add(1, 2, 3) << endl;        // 调用 int add(int, int, int)
    cout << calc.add("Hello", " World") << endl; // 调用 string add(string, string)
    
    return 0;
}
  • 运算符重载:主要运用于需要对自定义数据类型进行运算符操作时。

#include <iostream>
using namespace std;

class Complex {
private:
    double real, imag;

public:
    Complex(double r = 0, double i = 0) : real(r), imag(i) {}
    
    // 重载 + 运算符
    Complex operator + (const Complex& other) const {
        return Complex(real + other.real, imag + other.imag);
    }
    
    // 重载 - 运算符
    Complex operator - (const Complex& other) const {
        return Complex(real - other.real, imag - other.imag);
    }
    
    // 重载 << 运算符(友元函数)
    friend ostream& operator << (ostream& os, const Complex& c) {
        os << c.real << " + " << c.imag << "i";
        return os;
    }
    
    // 重载 == 运算符
    bool operator == (const Complex& other) const {
        return (real == other.real) && (imag == other.imag);
    }
};

int main() {
    Complex c1(3.0, 4.0);
    Complex c2(1.0, 2.0);
    
    Complex sum = c1 + c2;    // 调用重载的 + 运算符
    Complex diff = c1 - c2;   // 调用重载的 - 运算符
    
    cout << "c1 = " << c1 << endl;
    cout << "c2 = " << c2 << endl;
    cout << "c1 + c2 = " << sum << endl;
    cout << "c1 - c2 = " << diff << endl;
    cout << "c1 == c2? " << (c1 == c2 ? "Yes" : "No") << endl;
    
    return 0;
}
  • 模板:用于适用于不同的输入类型

#include <iostream>
using namespace std;

// 函数模板 - 泛型编程
template <typename T>
T getMax(T a, T b) {
    return (a > b) ? a : b;
}

// 类模板
template <class T>
class Container {
private:
    T value;

public:
    Container(T v) : value(v) {}
    
    T getValue() const { return value; }
    void setValue(T v) { value = v; }
    
    void display() const {
        cout << "Value: " << value << endl;
    }
};

// 特化模板
template <>
class Container<char> {
private:
    char value;

public:
    Container(char v) : value(v) {}
    
    void display() const {
        cout << "Character: '" << value << "' (ASCII: " << (int)value << ")" << endl;
    }
};

int main() {
    // 函数模板使用
    cout << "Max of 5 and 3: " << getMax(5, 3) << endl;
    cout << "Max of 2.7 and 3.1: " << getMax(2.7, 3.1) << endl;
    cout << "Max of 'a' and 'z': " << getMax('a', 'z') << endl;
    
    // 类模板使用
    Container<int> intContainer(42);
    Container<double> doubleContainer(3.14);
    Container<string> stringContainer("Hello Template");
    Container<char> charContainer('A');
    
    intContainer.display();
    doubleContainer.display();
    stringContainer.display();
    charContainer.display();
    
    return 0;
}

3.3.2 运行时多态(动态多态)

  • 虚函数

#include <iostream>
#include <vector>
#include <memory>
using namespace std;

// 基类
class Animal {
protected:
    string name;

public:
    Animal(string n) : name(n) {}
    
    // 虚函数 - 允许派生类重写
    virtual void speak() const {
        cout << name << " makes a sound." << endl;
    }
    
    // 纯虚函数 - 抽象类,必须被派生类实现
    virtual void move() const = 0;
    
    virtual ~Animal() {
        cout << "Animal destructor: " << name << endl;
    }
};

// 派生类
class Dog : public Animal {
private:
    string breed;

public:
    Dog(string n, string b) : Animal(n), breed(b) {}
    
    void speak() const override {
        cout << name << " the " << breed << " says: Woof! Woof!" << endl;
    }
    
    void move() const override {
        cout << name << " is running on four legs." << endl;
    }
    
    ~Dog() override {
        cout << "Dog destructor: " << name << endl;
    }
};

class Bird : public Animal {
private:
    double wingspan;

public:
    Bird(string n, double w) : Animal(n), wingspan(w) {}
    
    void speak() const override {
        cout << name << " says: Tweet! Tweet!" << endl;
    }
    
    void move() const override {
        cout << name << " is flying with " << wingspan << "m wingspan." << endl;
    }
    
    ~Bird() override {
        cout << "Bird destructor: " << name << endl;
    }
};

class Fish : public Animal {
public:
    Fish(string n) : Animal(n) {}
    
    void speak() const override {
        cout << name << " says: Blub! Blub!" << endl;
    }
    
    void move() const override {
        cout << name << " is swimming." << endl;
    }
    
    ~Fish() override {
        cout << "Fish destructor: " << name << endl;
    }
};

// 使用多态的函数
void animalConcert(const vector<Animal*>& animals) {
    cout << "\n=== Animal Concert ===" << endl;
    for (const auto& animal : animals) {
        animal->speak();  // 动态绑定 - 运行时确定调用哪个speak()
    }
}

void animalMovement(const vector<Animal*>& animals) {
    cout << "\n=== Animal Movement ===" << endl;
    for (const auto& animal : animals) {
        animal->move();   // 动态绑定 - 运行时确定调用哪个move()
    }
}

int main() {
    // 创建不同类型的动物对象
    Dog dog("Buddy", "Golden Retriever");
    Bird bird("Tweety", 0.5);
    Fish fish("Nemo");
    
    // 通过基类指针调用 - 多态行为
    Animal* animals[] = {&dog, &bird, &fish};
    
    cout << "=== Direct Calls ===" << endl;
    for (int i = 0; i < 3; i++) {
        animals[i]->speak();  // 多态调用
        animals[i]->move();   // 多态调用
        cout << endl;
    }
    
    // 使用vector和智能指针
    vector<unique_ptr<Animal>> animalList;
    animalList.push_back(make_unique<Dog>("Max", "Bulldog"));
    animalList.push_back(make_unique<Bird>("Robin", 0.3));
    animalList.push_back(make_unique<Fish>("Dory"));
    
    cout << "=== Smart Pointer Calls ===" << endl;
    for (const auto& animal : animalList) {
        animal->speak();
        animal->move();
        cout << endl;
    }
    
    return 0;
}

3.3.3 常见问题

构造函数和析构函数可以是虚函数吗?为什么?

回答:

构造函数不能是虚函数,原因:

  1. 对象创建时机问题:虚函数表(vtable)是在构造函数执行期间建立的。在构造函数被调用时,对象还没有完全构造完成,虚函数机制尚未就绪。这里顺便解释一下什么是虚函数表:每个包含虚函数的类都有一个虚函数表,它是一个静态数组,存储了该类及其基类中所有虚函数的地址。当创建该类的对象时,对象中会包含一个指向该类虚函数表的指针(vptr)。当通过指针或引用调用虚函数时,程序会通过vptr找到对应的虚函数表,再通过虚函数表找到实际的函数地址。虚函数机制依赖于已构造的对象,而构造函数的任务就是创建这个对象,这是个"先有鸡还是先有蛋"的问题。

  2. 语义矛盾:虚函数用于实现运行时多态,但构造函数的目的是创建特定类型的对象。调用构造函数时,对象的类型已经明确,不需要动态绑定。

  3. 语法限制:C++标准明确规定构造函数不能是虚函数。

析构函数可以是虚函数

析构函数应该是虚函数的情况:
当类可能被继承,且可能通过基类指针删除派生类对象时,基类的析构函数必须声明为虚函数。

class Base {
public:
    virtual ~Base() {  // 正确:析构函数可以是虚函数
        cout << "Base destructor" << endl;
    }
};

class Derived : public Base {
public:
    ~Derived() override {
        cout << "Derived destructor" << endl;
    }
};

int main() {
    Base* ptr = new Derived();
    delete ptr;  // 正确调用 Derived 和 Base 的析构函数
    return 0;
}

为什么析构函数需要是虚函数?

如果基类析构函数不是虚函数,那么当用基类指针指向派生类对象时,该对象在析构时只会调用基类的虚函数:

class Base {
public:
    ~Base() {  // 非虚析构函数
        cout << "Base destructor" << endl;
    }
};

class Derived : public Base {
public:
    ~Derived() {
        cout << "Derived destructor" << endl;
    }
};

int main() {
    Base* ptr = new Derived();
    delete ptr;  // 只调用 Base 的析构函数!
    return 0;
}
派生类的构造函数和析构函数的执行过程?

构造过程:基类->成员类->派生类

析构过程:派生类 → 成员 → 基类

#include <iostream>
#include <string>
using namespace std;

// 基类
class Base {
private:
    string name;
public:
    Base() : name("Base") {
        cout << "Base默认构造函数: " << name << endl;
    }
    
    Base(const string& n) : name(n) {
        cout << "Base带参构造函数: " << name << endl;
    }
    
    virtual void show() {
        cout << "Base::show(): " << name << endl;
    }
    
    virtual ~Base() {
        cout << "Base析构函数: " << name << endl;
    }
};

// 成员类
class Member {
private:
    string name;
public:
    Member() : name("Member") {
        cout << "Member默认构造函数: " << name << endl;
    }
    
    Member(const string& n) : name(n) {
        cout << "Member带参构造函数: " << name << endl;
    }
    
    ~Member() {
        cout << "Member析构函数: " << name << endl;
    }
};

// 派生类
class Derived : public Base {
private:
    Member m1;
    Member m2;
    string name;
public:
    // 默认构造函数
    Derived() : Base("BaseFromDerived"), m1("FirstMember"), name("Derived") {
        cout << "Derived默认构造函数: " << name << endl;
        cout << "--- 构造完成 ---" << endl;
    }
    
    // 带参构造函数
    Derived(const string& baseName, const string& member1, const string& member2) 
        : Base(baseName), m1(member1), m2(member2), name("Derived") {
        cout << "Derived带参构造函数: " << name << endl;
        cout << "--- 构造完成 ---" << endl;
    }
    
    void show() override {
        cout << "Derived::show(): " << name << endl;
    }
    
    ~Derived() {
        cout << "--- 开始析构 ---" << endl;
        cout << "Derived析构函数: " << name << endl;
    }
};

// 测试函数
void testConstruction() {
    cout << "=== 创建默认Derived对象 ===" << endl;
    Derived d1;
    cout << endl;
    
    cout << "=== 创建带参Derived对象 ===" << endl;
    Derived d2("CustomBase", "CustomMember1", "CustomMember2");
    cout << endl;
    
    cout << "=== 多态测试 ===" << endl;
    Base* ptr = new Derived("PolyBase", "PolyMember1", "PolyMember2");
    ptr->show();
    delete ptr;  // 重要:由于Base有虚析构函数,会正确调用Derived的析构函数
    cout << endl;
    
    cout << "=== 局部对象即将离开作用域 ===" << endl;
}

int main() {
    testConstruction();
    cout << "=== testConstruction函数结束 ===" << endl;
    return 0;
}

3.3.4 总结对比

特性编译时多态运行时多态
确定时机编译时运行时
实现机制函数重载、运算符重载、模板虚函数、继承
性能高效,无额外开销有轻微性能开销(虚表查找)
灵活性相对较低高,支持动态绑定
典型应用数学运算、通用算法图形系统、游戏开发、框架设计

关键区别:

  • 编译时多态:编译器在编译时就知道调用哪个具体函数

  • 运行时多态:程序运行时根据对象的实际类型决定调用哪个函数

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

10710

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值