C++ 将基类的状态升级为继承类的全新实例

C++ 将基类的状态升级为继承类的全新实例,c++,inheritance,memory-management,C++,Inheritance,Memory Management,我想获取一个使用基类构造函数创建的BaseClass实例,并将其升级为Inherited实例。我知道继承的类需要为其分配单独的内存块,并且应该释放原始基类实例 这是一个(似乎)有效的例子 class BaseClass{ public: BaseClass(int mem1, int mem2){ this->mem1 = mem1; this->mem2 = mem2; } BaseClass(BaseClass* base

我想获取一个使用基类构造函数创建的
BaseClass
实例,并将其升级为
Inherited
实例。我知道继承的类需要为其分配单独的内存块,并且应该释放原始基类实例

这是一个(似乎)有效的例子

class BaseClass{
public:
    BaseClass(int mem1, int mem2){
        this->mem1 = mem1;
        this->mem2 = mem2;
    }

    BaseClass(BaseClass* base){
        this->mem1 = base->mem1;
        this->mem2 = base->mem2;
    }

    virtual ~BaseClass(){
        printf("Deconstruct Base\n");
    }

private:
    int mem1;
    int mem2;
};

class Inherited: public BaseClass{
public:
    Inherited(int mem1, int mem2, int mem3): BaseClass(mem1, mem2){
        this->mem3 = mem3;
    }

    Inherited(BaseClass* base, int mem3): BaseClass(base){
        this->mem3 = mem3;
        free(base); // this doesn't print 'Deconstruct Base'
    }

    ~Inherited(){
        printf("Deconstruct Inherited\n");
    }

    int mem3;
};

int main(){
    BaseClass* p = new BaseClass(1, 2);
    p = new Inherited(p, 3);

    // this prints 'Deconstruct Inherited' then 'Deconstruct Base'
    delete p; 
}
因此,在继承类的构造函数中,我使用
free
释放
BaseClass
的原始实例的内存

这样做的目的是,如果
BaseClass
有一个指向一大块动态分配内存的指针,我希望继承的类拾取该指针,而不是让基类释放它

我的问题是:

这会因为任何原因泄漏内存吗


有没有更干净的方法?(例如:调用
操作符delete
会比
免费的
更好吗?或者完全有更好的解决方案吗?

您无法更改对象的类型,因此很难知道“升级对象”的含义。但从代码来看,它或多或少是“将原始对象中的数据复制到新对象中,并除去原始对象”。这很简单:

class base {
public:
    base(int m) : mem1(m) {}
    base(const base&) = default;
private:
    int mem1;
};

class derived : public base {
public:
    derived(int m1, int m2) : base(m1), mem2(m2) {}
    derived(const base* b, int m2) : base(*b), mem2(m2) {}
private:
    int m2;
};

base* bp = new base(3);
base* temp = bp;
bp = new derived(temp, 4);
delete temp;
如果太冗长,可以使用描述性名称编写函数:

void replace_object(base*& bp, int m2) {
    base* temp = bp;
    bp = new derived(temp, m2);
    delete temp;
}
现在你可以写作了

base* bp = new base(3);
replace_object(bp, 4);
base* bp = new base(3);
bp = new derived(bp, 4);
如果您真的非常想隐藏所有这些东西(维护人员会讨厌您),您可以在
派生的
的构造函数中执行删除操作:

derived(const base* b, int m2) : base(*b), mem2(m2) {
    delete b;
}
现在你可以写作了

base* bp = new base(3);
replace_object(bp, 4);
base* bp = new base(3);
bp = new derived(bp, 4);

你的代码有很多问题

我建议您阅读Sutter中有关编写优秀代码的书籍,如Exception C++

第1期:在适当的时候使用初始化列表

BaseClass(int mem1, int mem2)
    : mem1(mem1)
    , mem2(mem2)
{
}

Inherited(int mem1, int mem2, int mem3)
    : BaseClass(mem1, mem2)
    , mem3(mem3)
{
}
这对于用户定义的类型和 不支持赋值(常量和引用)的类型 成员等)

始终在其他声明中初始化这些成员

第2期:定义标准副本构造函数(而不是您自己的变体)

甚至更好。在这种情况下,可以使用默认的复制构造函数

BaseClass(const BaseClass &other) = default;
Inherited(const BaseClass &b, int m3) 
    : BaseClass(b)
    , mem3(m3) 
{
}
问题3:如果定义复制构造函数,还应定义赋值运算符

BaseClass& operator=(const BaseClass &other) = default;
您还可以根据需要定义移动构造函数和赋值

第4期:您不应该对分配了
new
的对象调用
free

问题5:在大多数情况下,您应该避免通过构造函数转移所有权

BaseClass(const BaseClass &other) = default;
Inherited(const BaseClass &b, int m3) 
    : BaseClass(b)
    , mem3(m3) 
{
}
这很容易出错,因为这不是用户所期望的。这使得代码更难维护,如果您不小心,如果构造函数抛出异常,这可能是内存泄漏的来源

BaseClass(const BaseClass &other) = default;
Inherited(const BaseClass &b, int m3) 
    : BaseClass(b)
    , mem3(m3) 
{
}
第6期:如果您真的想转移所有权,那么您应该使用
std::unique\u ptr
来明确您正在转移所有权

Inherited(std::uniquer_ptr<BaseClass> b, int m3) 
    : BaseClass(*b)
    , mem3(m3) 
{
}
如果以后不再需要
b
,您也可以编写如下内容:

std::unique_ptr<BaseClass> p = std::make_unique<BaseClass>(1, 2);
std::unique_ptr<Inherited> q = std::make_unique<Inherited>(std::move(p), 3);
// at this point, p is null since ownership has been moved
p = q;
Inherited i2(Base(1, 2), 3);
在这一点上,为什么不写:

Inherited i3(1, 2, 3);
第8期:在升级对象类型时不存在此类问题。您只需将新值分配给基于旧对象的指针


实际上替换一个对象会导致对象切片。

调用
free
而不是
delete
是未定义的行为,但您应该使用智能指针。每个
新的
都需要
delete
@Quentin有关
free
调用的良好信息,但我不认为我在寻找聪明的指针。我修改了问题,详细说明了我不希望解构器在升级时调用
基类
。这里最重要的问题是为什么要使用原始指针和
新建
/
删除
?没有任何东西可以升级对象。您可以做的是您已经尝试过的,从
基类
构造一个
继承的
。试图在构造函数中混合内存清理看起来非常糟糕。使用智能指针。这里有很多设计问题。首先,构造表单
BaseClass常量&
BaseClass&
而不是
BaseClass*
。另外,我想知道为什么
BaseClass
甚至有任何公共构造函数。感谢您深思熟虑的回答。我真的不喜欢从
派生的
构造函数中删除
b
的建议。这很容易出错。如果你想转移所有权,就应该使用std::unique_ptr
。@Phil1970——这就是为什么我在介绍时说“如果你真的,真的想隐藏所有这些东西(维护者会讨厌你)…”。