简体   繁体   中英

Why there is something wrong when invoking ctor in another ctor?

Here is a demo code snippet( 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();
}

Here are outputs when invoking the binary program on Ubuntu:

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

You see the addresses are not the same. I have set m_L by invoking Ctx::Ctx() (which is called by Ctx::Ctx(RUN_MOD runType) ) indeed. I am really confused.

If you are looking for a clean solution you could use a function that does, what the default constructor should do. This way you don't need constructor delegation, which afaik can't be applied conditional and you don't have to mess with placement new.

Minimal example:

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();
    }
}

Constructor delegation can only be used from inside a constructor's member initialization list , NOT inside a constructor's body. This answer shows how to avoid the need to use constructor delegation at all, by creating a common initialization function that multiple constructors can then call as needed.

However, if you did want to use constructor delegation, for whatever reason, then in this case the Ctx() constructor should delegate to the Ctx(RUN_MOD) constructor, not the other way around as you originally attempted, eg:

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";
};

Calling ctor of any class makes an instance of it, in your posted code it is obvious that allocated m_l in Ctx() is not belong to the object constructed by Ctx(RUN_MOD runType)

try below code

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();
}

I found a solution, here is the code snippet( https://coliru.stacked-crooked.com/a/964309f60f62dbd4 , it works, but I am still trying to understand it):

#include<iostream>

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

    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();
}

It's a good answer, but the author deleted it. I posted it here for more people to study.

his code:

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

creates a temporary Ctx() object, and -- that's it. Nothing more, nothing less. That temporary just gets destroyed as soon as that line of code is finished executing.

There are other major issues with the class, such as using malloc, the class leaks memory due to not following the rule of 3, not initializing all the members (which I addressed by setting m_L to nullptr ), etc.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM