C++复习

130 阅读6分钟

虚函数

实现多态的核心机制,通过基类指针调用,实际上调用的对象实际类型的实现
包含虚函数的类都有一个虚函数表,该表是一个虚函数指针数组(指针数组),储存所有虚函数的地址
类对象中包含一个隐藏虚表指针(vptr)指向类的虚函数表

class Base {
public:
    virtual void f1() {}
    virtual void f2() {}
    int a;
};

class Derived : public Base {
public:
    void f1() override {}
    virtual void f3() {}
    int b;
};

C++ 虚函数机制详解

虚函数是 C++ 实现运行时多态的核心机制,它允许通过基类指针或引用调用派生类的函数实现。下面我将全面解析虚函数的实现机制和工作原理。

1. 虚函数的基本概念

虚函数声明

cpp
复制
class Base {
public:
    virtual void func(); // 虚函数声明
    virtual ~Base();     // 虚析构函数
};

虚函数特点

  • 使用 virtual 关键字声明
  • 派生类可以重写(override)虚函数
  • 通过基类指针/引用调用时,实际调用的是对象实际类型的实现

2. 虚函数表(vtable)机制

虚函数表结构

  • 每个包含虚函数的类都有一个虚函数表
  • 虚函数表是一个函数指针数组,存储该类所有虚函数的地址
  • 类对象中包含一个隐藏的虚表指针(vptr),指向对应的虚函数表

内存布局示例

cpp
复制
class Base {
public:
    virtual void f1() {}
    virtual void f2() {}
    int a;
};

class Derived : public Base {
public:
    void f1() override {}
    virtual void f3() {}
    int b;
};

内存布局:

Base对象:
+---------+
| vptr    | -> 指向Base的vtable [&Base::f1, &Base::f2]
+---------+
| a       |
+---------+

Derived对象:
+---------+
| vptr    | -> 指向Derived的vtable [&Derived::f1, &Base::f2, &Derived::f3]
+---------+
| a       |
+---------+
| b       |
+---------+

虚函数调用过程:
basePtr->f1()
通过对象找到vptr,vptr找到vtable,在vtable找到f1,调用f1

构造函数顺序

基类构造函数-> 成员对象构造函数 -> 派生类构造函数

class Base {
public:
    Base() { cout << "Base constructor" << endl; }
};

class Member {
public:
    Member() { cout << "Member constructor" << endl; }
};

class Derived : public Base {
    Member m;
public:
    Derived() { cout << "Derived constructor" << endl; }
};

// 创建Derived对象时的输出顺序:
// Base constructor
// Member constructor
// Derived constructor

多继承情况下

class Derived : public Base1, public Base2 {
    // Base1先构造,然后Base2
};

为什么有的析构函数要设置为虚函数

虚继承

菱形继承

class Base {
public:
    int data;
};

class Derived1 : public Base { /*...*/ };
class Derived2 : public Base { /*...*/ };

class Final : public Derived1, public Derived2 {
    // 此时Final对象中包含两个Base子对象
};

存在问题:data具有二义性
解决

class Base {
public:
    int data;
};
class Derived1 : virtual public Base { /*...*/ };
class Derived2 : virtual public Base { /*...*/ };

class Final : public Derived1, public Derived2 {
    // 现在Final对象中只有一个Base子对象
};

虚继承的效果
共享基类:无论虚基类在继承层次中出现多少次,派生类中只包含一个共享的基类子对象 消除二义性:可以直接访问基类成员,无需指定路径

拷贝构造函数

拷贝构造函数时C++一种特殊的构造函数,用于创建一个新对象作为现有对象的副本。她在其下情况下被调用: 1.用一个对象初始化另一个对象时 2.对象作为函数参数按值传递时 3.函数返回对象时

#include <iostream>
#include <cstring> // for strcpy

class String {
private:
    char* data;
    size_t length;

public:
    // 普通构造函数
    String(const char* str = "") {
        length = strlen(str);
        data = new char[length + 1];
        strcpy(data, str);
        std::cout << "Constructed: " << data << std::endl;
    }

    // 拷贝构造函数
    String(const String& other) {
        length = other.length;
        data = new char[length + 1];
        strcpy(data, other.data);
        std::cout << "Copied: " << data << std::endl;
    }

    // 析构函数
    ~String() {
        delete[] data;
        std::cout << "Destructed" << std::endl;
    }

    // 赋值运算符
    String& operator=(const String& other) {
        if (this != &other) {
            delete[] data;
            length = other.length;
            data = new char[length + 1];
            strcpy(data, other.data);
            std::cout << "Assigned: " << data << std::endl;
        }
        return *this;
    }

    void print() const {
        std::cout << "String: " << data << " (" << length << ")" << std::endl;
    }
};

void testFunction(String s) {
    std::cout << "Inside function: ";
    s.print();
}

int main() {
    String s1("Hello");
    String s2 = s1;  // 调用拷贝构造函数
    
    std::cout << "\nTesting assignment:\n";
    String s3("World");
    s3 = s1;  // 调用赋值运算符
    
    std::cout << "\nTesting function call:\n";
    testFunction(s1);  // 调用拷贝构造函数
    
    return 0;
}

拷贝构造函数和赋值运算符,如果没有定义,编译器会生成一个默认的(执行成员逐个拷贝) 拷贝构造函数和赋值运算符区别
赋值运算符是两个已存在对象之间的赋值。

MyClass a;
MyClass b(a);  // 调用拷贝构造函数
MyClass c = a; // 调用拷贝构造函数
MyClass a, b;
a = b;  // 调用赋值运算符

实现shared_ptr unique_ptr

#include <iostream>
#include <utility> // for std::move

// 类模板
template <typename T>
class shared_ptr {
private:
    T* ptr = nullptr;
    size_t* count = nullptr; // 引用计数器

    void release() {
        if (count && --(*count) == 0) {
            delete ptr;
            delete count;
        }
        ptr = nullptr;
        count = nullptr;
    }

public:
    // 构造函数,p为默认参数,若未提供,则使用nullptr
    explicit shared_ptr(T* p = nullptr) : ptr(p), count(new size_t(1)) {}
    
    // 拷贝构造
    shared_ptr(const shared_ptr& other) : ptr(other.ptr), count(other.count) {
        if (count) ++(*count);
    }
    
    // 拷贝赋值
    shared_ptr& operator=(const shared_ptr& other) {
        if (this != &other) {
            release();
            ptr = other.ptr;
            count = other.count;
            if (count) ++(*count);
        }
        return *this;
    }
    
    // 移动构造
    shared_ptr(shared_ptr&& other) noexcept : ptr(other.ptr), count(other.count) {
        other.ptr = nullptr;
        other.count = nullptr;
    }
    
    // 移动赋值
    shared_ptr& operator=(shared_ptr&& other) noexcept {
        if (this != &other) {
            release();
            ptr = other.ptr;
            count = other.count;
            other.ptr = nullptr;
            other.count = nullptr;
        }
        return *this;
    }
    
    // 析构函数
    ~shared_ptr() {
        release();
    }
    
    // 操作符重载
    T& operator*() const { return *ptr; }
    T* operator->() const { return ptr; }
    explicit operator bool() const { return ptr != nullptr; }
    
    // 获取原始指针
    T* get() const { return ptr; }
    
    // 获取引用计数
    size_t use_count() const { return count ? *count : 0; }
    
    // 重置指针
    void reset(T* p = nullptr) {
        release();
        ptr = p;
        count = new size_t(1);
    }
};

// 测试代码
void test_shared_ptr() {
    std::cout << "\n=== Testing shared_ptr ===" << std::endl;
    
    shared_ptr<int> sp1(new int(100));
    std::cout << "sp1 value: " << *sp1 << ", count: " << sp1.use_count() << std::endl;
    
    {
        shared_ptr<int> sp2 = sp1; // 拷贝构造
        std::cout << "After copy:" << std::endl;
        std::cout << "sp1 count: " << sp1.use_count() << std::endl;
        std::cout << "sp2 value: " << *sp2 << ", count: " << sp2.use_count() << std::endl;
        
        shared_ptr<int> sp3(new int(200));
        sp2 = sp3; // 拷贝赋值
        std::cout << "After assignment:" << std::endl;
        std::cout << "sp1 count: " << sp1.use_count() << std::endl;
        std::cout << "sp2 count: " << sp2.use_count() << std::endl;
        std::cout << "sp3 count: " << sp3.use_count() << std::endl;
    } // sp2, sp3 析构
    
    shared_ptr<int> sp4 = std::move(sp1); // 移动构造
    std::cout << "After move:" << std::endl;
    std::cout << "sp1 is " << (sp1 ? "not null" : "null") << std::endl;
    std::cout << "sp4 value: " << *sp4 << ", count: " << sp4.use_count() << std::endl;
}

int main() {
    test_shared_ptr();
    return 0;
}
#include <iostream>
#include <utility> // for std::move

template <typename T>
class unique_ptr {
private:
    T* ptr = nullptr;

public:
    // 构造函数
    explicit unique_ptr(T* p = nullptr) : ptr(p) {}
    
    // 禁止拷贝
    unique_ptr(const unique_ptr&) = delete;
    unique_ptr& operator=(const unique_ptr&) = delete;
    
    // 移动构造
    unique_ptr(unique_ptr&& other) noexcept : ptr(other.ptr) {
        other.ptr = nullptr;
    }
    
    // 移动赋值
    unique_ptr& operator=(unique_ptr&& other) noexcept {
        if (this != &other) {
            delete ptr;
            ptr = other.ptr;
            other.ptr = nullptr;
        }
        return *this;
    }
    
    // 析构函数
    ~unique_ptr() {
        delete ptr;
    }
    
    // 操作符重载
    T& operator*() const { return *ptr; }
    T* operator->() const { return ptr; }
    explicit operator bool() const { return ptr != nullptr; }
    
    // 获取原始指针
    T* get() const { return ptr; }
    
    // 释放所有权
    T* release() {
        T* p = ptr;
        ptr = nullptr;
        return p;
    }
    
    // 重置指针
    void reset(T* p = nullptr) {
        delete ptr;
        ptr = p;
    }
};

// 测试代码
void test_unique_ptr() {
    std::cout << "=== Testing unique_ptr ===" << std::endl;
    
    unique_ptr<int> p1(new int(42));
    std::cout << "p1 value: " << *p1 << std::endl;
    
    unique_ptr<int> p2 = std::move(p1); // 所有权转移
    std::cout << "After move:" << std::endl;
    std::cout << "p1 is " << (p1 ? "not null" : "null") << std::endl;
    std::cout << "p2 value: " << *p2 << std::endl;
    
    p2.reset(new int(100));
    std::cout << "After reset: " << *p2 << std::endl;
    
    int* raw_ptr = p2.release();
    std::cout << "Released raw pointer value: " << *raw_ptr << std::endl;
    delete raw_ptr;
}


int main() {
    test_unique_ptr();
    return 0;
}