为什么在另一个 ctor 中调用 ctor 会出错?

问题描述

这是一个演示代码片段(https://godbolt.org/z/31Tq3r):

 #include<iostream>

class Ctx
{
public:
    enum RUN_MOD
    {
        MOD_RT,MOD_NRT,};

    Ctx(RUN_MOD runType)
    {
        if(runType == MOD_RT)
        {
            Ctx();
        }
    }

    Ctx()
    {
        m_L = malloc(100);
        std::cout << "set m_L=" << m_L << std::endl;
    }

    void print()
    {
        std::cout <<"print() m_L=" << m_L << std::endl;
    }

private:
    void *m_L;

    const char* const ARG_TYPE_NOT_MATCH = "the type of argument is not match";
    const char* const ARG_NUM_INVALID = "the number of arguments is invalid";
    const char* const STACK_OPS_INPUT_ARG_INVALID = "the input argument passed to the stack ops is invalid";
};


int main()
{
    Ctx ctx(Ctx::RUN_MOD::MOD_RT);

    ctx.print();
}

以下是在 Ubuntu 上调用二进制程序时的输出

set m_L=0x614c20 
print() m_L=0x400ad0

你看地址不一样。我确实通过调用 m_L(由 Ctx::Ctx() 调用)设置了 Ctx::Ctx(RUN_MOD runType)。我真的很困惑。

解决方法

如果你正在寻找一个干净的解决方案,你可以使用一个函数,默认构造函数应该做什么。这样你就不需要构造函数委托,它不能被有条件地应用,你也不必弄乱放置 new。

最小示例:

class Ctx
{
private:
    void DefaultInit()
    {
        m_L = malloc(100);
        std::cout << "set m_L=" << m_L << std::endl;
    } 

public:
    Ctx(RUN_MOD runType)
    {
        if(runType == MOD_RT)
        {
            this->DefaultInit();
        }
    }

    Ctx()
    {
       this->DefaultInit();
    }
}
,

构造函数委托只能在构造函数的 member initialization list 内部使用,不能在构造函数体内使用。 This answer 展示了如何完全避免使用构造函数委托,方法是创建一个通用的初始化函数,然后多个构造函数可以根据需要调用该函数。

但是,如果您确实想使用构造函数委托,无论出于何种原因,那么在这种情况下,Ctx() 构造函数应委托给 Ctx(RUN_MOD) 构造函数,而不是您最初尝试的相反方式,例如:

class Ctx
{
public:
    enum RUN_MOD
    {
        MOD_RT,MOD_NRT,};

    Ctx(RUN_MOD runType)
    {
        if (runType == MOD_RT)
        {
            m_L = malloc(100);
            std::cout << "set m_L=" << m_L << std::endl;
        }
    }

    Ctx() : Ctx(MOD_RT) // <— here
    {
    }

    void print()
    {
        std::cout << "print() m_L=" << m_L << std::endl;
    }

private:
    void *m_L = nullptr;

    const char* const ARG_TYPE_NOT_MATCH = "the type of argument is not match";
    const char* const ARG_NUM_INVALID = "the number of arguments is invalid";
    const char* const STACK_OPS_INPUT_ARG_INVALID = "the input argument passed to the stack ops is invalid";
};
,

调用任何类的 ctor 都会生成它的一个实例,在您发布的代码中,很明显 Ctx() 中分配的 m_l 不属于由构造的对象Ctx(RUN_MOD runType)

试试下面的代码

void init_m_l()
{
    m_L = malloc(100);
    std::cout << "set m_L=" << m_L << std::endl;
}

Ctx(RUN_MOD runType)
{
    if(runType == MOD_RT)
       init_m_l();
}

Ctx()
{
    init_m_l();
}
,

我找到了一个解决方案,这是代码片段(https://coliru.stacked-crooked.com/a/964309f60f62dbd4,它有效,但我仍在努力理解它):

#include<iostream>

class Ctx
{
public:
    enum RUN_MOD
    {
        MOD_RT,};

    Ctx(RUN_MOD runType)
    {
        if(runType == MOD_RT)
        {
            new (this)Ctx();
        }
    }

    Ctx()
    {
        m_L = malloc(100);
        std::cout << "set m_L=" << m_L << std::endl;
    }

    void print()
    {
        std::cout <<"print() m_L=" << m_L << std::endl;
    }

    ~Ctx()
    {
        free(m_L);
    }

    Ctx(const Ctx&) = delete;  //to avoid double free

private:
    void *m_L;

    const char* const ARG_TYPE_NOT_MATCH = "the type of argument is not match";
    const char* const ARG_NUM_INVALID = "the number of arguments is invalid";
    const char* const STACK_OPS_INPUT_ARG_INVALID = "the input argument passed to the stack ops is invalid";
};


int main()
{
    Ctx ctx(Ctx::RUN_MOD::MOD_RT);

    ctx.print();
}
,

这是一个很好的答案,但作者删除了它。 我把它贴在这里供更多人学习。

他的代码:

if(runType == MOD_RT)
{
    Ctx();
}

创建一个临时的 Ctx() 对象,然后——就是这样。不多也不少。一旦该行代码执行完毕,该临时文件就会被销毁。

该类还有其他主要问题,例如使用 malloc,该类由于不遵循 3 规则而导致内存泄漏,未初始化所有成员(我通过将 m_L 设置为 {{1 }}) 等