这里有一些代码希望能帮到你。
Derived 不拥有动态资源
Base 类拥有一个动态资源,因此我们需要遵循 3 规则(应该是 5,但为简洁起见保持为 3)。我通过使用复制/交换习语来做到这一点。
然后我从Base 派生Derived。它不保存动态资源,所以我遵循 0 规则,不提供自定义复制构造函数、析构函数、赋值运算符、移动构造函数或移动赋值。
您可以从输出中看到Derived 对象的Base 部分能够很好地深度复制它们,而Derived-only 部分可以很好地使用浅层复制。最终输出会泄漏内存,但我选择这样做是为了演示使用指向 Base 的指针进行实际覆盖。
#include <iostream>
class Base {
private:
int* m = nullptr;
public:
Base() = default;
Base(int v) : m(new int(v)) {}
Base(const Base& other) : m(new int(*(other.m))) {}
virtual ~Base() {
delete m;
m = nullptr;
}
Base& operator=(Base other) {
swap(*this, other);
return *this;
}
friend void swap(Base& lhs, Base& rhs) {
using std::swap;
swap(lhs.m, rhs.m);
}
virtual void print() const {
std::cout << "Address: " << m << "\nValue: " << *m << '\n';
}
};
class Derived : public Base {
private:
double x = 0.0;
public:
Derived() = default;
Derived(double v) : Base(), x(v) {}
Derived(int i, double v) : Base(i), x(v) {}
void print() const override {
std::cout << "Address: " << &x << "\nValue: " << x << '\n';
Base::print();
}
};
int main() {
std::cout << "A\n";
Base* a = new Derived(5, 3.14);
a->print();
std::cout << "\nB\n";
Derived b = *(dynamic_cast<Derived*>(a)); // Copy ctor
b.print();
std::cout << "\nC\n";
Derived c;
c = b;
c.print();
std::cout << "\nReplace A (This leaks)\n";
a = new Derived(7, 9.81);
a->print();
}
输出:
A
Address: 0x21712d0
Value: 3.14
Address: 0x21712e0
Value: 5
B
Address: 0x7ffdd62964c8
Value: 3.14
Address: 0x2171300
Value: 5
C
Address: 0x7ffdd62964b0
Value: 3.14
Address: 0x2171320
Value: 5
Replace A (This leaks)
Address: 0x2171350
Value: 9.81
Address: 0x2171360
Value: 7
Derived 拥有动态资源
现在,Derived 拥有自己的动态来管理。所以我遵循规则 3 并提供复制构造函数、析构函数和赋值运算符重载。您会注意到赋值运算符看起来与Base 版本相同;这是故意的。
这是因为我使用的是复制/交换习语。所以在 Derived 的 swap() 函数中,我添加了一个步骤,它交换 Base 部分,然后交换 Derived 部分。我通过动态转换调用 Base swap() 函数来做到这一点。
我们可以再次观察到,所有对象对于每个动态分配的块都有自己的内存。
#include <iostream>
class Base {
private:
int* m = nullptr;
public:
Base() = default;
Base(int v) : m(new int(v)) {}
Base(const Base& other) : m(new int(*(other.m))) {}
virtual ~Base() {
delete m;
m = nullptr;
}
Base& operator=(Base other) {
swap(*this, other);
return *this;
}
friend void swap(Base& lhs, Base& rhs) {
using std::swap;
swap(lhs.m, rhs.m);
}
virtual void print() const {
std::cout << "Address: " << m << "\nValue: " << *m << '\n';
}
};
class Derived : public Base {
private:
double* x = nullptr;
public:
Derived() = default;
Derived(double v) : Base(), x(new double(v)) {}
Derived(int i, double v) : Base(i), x(new double(v)) {}
Derived(const Derived& other) : Base(other), x(new double(*(other.x))) {}
~Derived() {
delete x;
x = nullptr;
}
Derived& operator=(Derived other) {
swap(*this, other);
return *this;
}
friend void swap(Derived& lhs, Derived& rhs) {
using std::swap;
swap(dynamic_cast<Base&>(lhs), dynamic_cast<Base&>(rhs));
swap(lhs.x, rhs.x);
}
void print() const override {
std::cout << "Address: " << &x << "\nValue: " << *x << '\n';
Base::print();
}
};
int main() {
std::cout << "A\n";
Base* a = new Derived(5, 3.14);
a->print();
std::cout << "\nB\n";
Derived b = *(dynamic_cast<Derived*>(a)); // Copy ctor
b.print();
std::cout << "\nC\n";
Derived c;
c = b;
c.print();
std::cout << "\nReplace A (This leaks)\n";
a = new Derived(7, 9.81);
a->print();
}
输出:
A
Address: 0x14812d0
Value: 3.14
Address: 0x14812e0
Value: 5
B
Address: 0x7fffe89e8d68
Value: 3.14
Address: 0x1481320
Value: 5
C
Address: 0x7fffe89e8d50
Value: 3.14
Address: 0x1481360
Value: 5
Replace A (This leaks)
Address: 0x14813b0
Value: 9.81
Address: 0x14813c0
Value: 7