虚函数
实现多态的核心机制,通过基类指针调用,实际上调用的对象实际类型的实现
包含虚函数的类都有一个虚函数表,该表是一个虚函数指针数组(指针数组),储存所有虚函数的地址
类对象中包含一个隐藏虚表指针(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;
}