类包含复制构造函数和赋值运算符时的继承行为

Posted

技术标签:

【中文标题】类包含复制构造函数和赋值运算符时的继承行为【英文标题】:Behaviour of Inheritance when class includes copy constructor and assignment operator 【发布时间】:2012-12-18 16:01:18 【问题描述】:

我无法理解这种行为。我有一个A类,

class A

public:
int ch;
char *s;
A()

A(char *st):ch(0)

    s = new char[10];
    strcpy(s,st);


A(const A& rhs):ch(rhs.ch)

    s = new char[strlen(rhs.s)+1];
    strcpy(s,rhs.s);

const A& operator=(const A& rhs)

    char *temp = new char[strlen(rhs.s)+1];
    strcpy(temp,rhs.s);
    delete[] s;
    s=temp;
    ch = rhs.ch;
    return *this;

~A()

    delete []s;

;

到目前为止,一切都按预期进行,我可以测试我的复制构造函数和赋值运算符,它们工作正常。

现在我创建了一个子类 B,但我遇到了堆损坏错误。我无法理解,这是与 A 类析构函数相关的问题吗? ? 下面是我的B班,

class B:public A


public:
int a;
B():a(0)
;

【问题讨论】:

您可能从未使用过A 的默认构造函数,直到它被B 隐式使用。问题是默认构造函数未初始化 s,因此它指向任何地方,然后当析构函数进入并尝试 delete [] 它时,您会崩溃。 当你调用 only 构造函数时是否会发生损坏? @Mark 是的意思是当我试图访问 B 类对象时 @jogojapan 是的,可能是这样,让我再检查一遍 您的赋值运算符是正确的(尽管您可能不应该养成在对象状态最终确定之前调用 delete 的习惯(删除时可能会抛出非 POD))。但更简单的编写方法是使用Copy and Swap idiom 【参考方案1】:

A 的默认构造函数没有初始化成员 s(一个指针):

A()

因此,当使用此构造函数构造元素时,当析构函数删除未初始化的元素时会崩溃:

~A()

  delete []s;

B 类使用A 的默认构造函数,因此会触发此问题。通过在默认构造函数中正确初始化所有成员来避免它:

A() : ch(), s(0)
 

【讨论】:

是的,谢谢,这是我错了..我很傻:( ...我正在删除未初始化的成员..谢谢。!【参考方案2】:

要解决您的问题,您需要做的就是替换:

char *s;

std::string s;

只需通过char *摆脱手动内存管理,这正是C++为您提供std::string的原因。

可能是什么问题?

不带任何参数的默认构造函数不会进行动态分配。 如果你通过这个构造函数创建了类对象,你的析构函数最终会delete指向一个没有被new分配的指针,从而导致未定义的行为。

【讨论】:

是的,这是真的,但实际上我正在练习复制构造函数和赋值运算符! @Himank:你在练习写不好的代码吗?你不应该!你应该练习编写好的代码。 实际上我正在讨论为什么我们需要复制构造函数和赋值运算符..所以我尝试了这个涉及原始指针成员的示例 否则我也更喜欢字符串.. :)【参考方案3】:

在析构函数中,你delete[] s;,但在默认构造函数中,你没有new[]ed。事实上,你甚至还没有初始化s

基类的默认构造函数在你实例化派生类时被调用,因为你没有初始化基类(: A(...))。因此,您不知道要删除什么,甚至不知道明天早餐要吃什么,因为这是未定义的行为。

为了保持一致,new[] s 在默认构造函数中。为了避免头痛,我建议使用 std::string 之类的东西,而不是字符指针。

【讨论】:

以上是关于类包含复制构造函数和赋值运算符时的继承行为的主要内容,如果未能解决你的问题,请参考以下文章

包含指向派生模板类的基类指针的类的赋值运算符和复制构造函数

C++的探索路12继承与派生之高级篇--派生类与赋值运算符及多重继承

依赖项没有复制 ctor 或赋值运算符时的 C++ 初始化程序列表

c++中拷贝构造函数和赋值运算符重载本质上一样么

复制构造函数与赋值运算符(=)有何不同

c++ 拷贝构造函数与赋值运算符重载函数的区别是