q0987
q0987

Reputation: 35982

defer the initalization of boost::scoped_ptr

I have defined a member variable as follows. Since the variable will not be passed around, so I decide to use scoped_ptr rather than shared_ptr here.

class ClassName
{
public:
    ClassName() 
    {
        Initialize();
    }

    virtual void Initialize() = 0;

protected:
    boost::scoped_ptr<int> m_scpInt;
}

class ClassNameB : public ClassName
{
public:
    virtual void Initialize()
    {
        m_scpInt.reset(new int(100));
    }
}

Due to the limit of the scoped_ptr, if I decide to defer the initialization of the variable in a later time, the only option I get is to call reset.

Q1> Is this a good practice?

Q2> Otherwise, is there a better solution?

Thank you

/// Updated -1 ///

This is what I really want to do.

I want to enforce that each of the derived class define a function called Initialize and which in turn calls function InitializeVarA and InitializeVarB. As you indicate, that we cannot call virtual function in the constructor.

class ClassName
{
public:
    ClassName() 
    {
    }       
    virtual void Initialize()
        {
            InitializeVarA();
            InitializeVarB();
        }           
    protected:
        virtual void InitializeVarA() {}
        virtual void InitializeVarB() {}
}

class ClassNameB : public ClassName
{
public:
    ClassNameB() 
    {
    }       
    virtual void Initialize()
        {
            InitializeVarA();
            InitializeVarB();
        }           
    protected:
        virtual void InitializeVarA() {}
        virtual void InitializeVarB() {}
}

ClassNameB cb;
cb.Initialize();

Do I have a better solution than this?

Upvotes: 1

Views: 723

Answers (1)

Mike Seymour
Mike Seymour

Reputation: 254431

Is this a good practice?

Using reset to reset a scoped pointer is fine.

Trying to initialise a derived class by calling a virtual function from the base class's constructor is not just bad practice; it's wrong. At that point, the dynamic type of the object is the base class, and the function is pure virtual, so calling it gives undefined behaviour.

Even if you made it non-pure, you still can't call the derived class's override at that point, so the pointer won't be reset.

Otherwise, is there a better solution?

You could do it in the derived class's constructor, which is invoked immediately after the base class's:

class Base {
public:
    Base() { /* don't call any virtual functions here */ }

protected:
    boost::scoped_ptr<int> p;
};

class Derived : public Base {
public:
    Derived() {
        p.reset(new int(100));
    }
};

Or you could pass the allocated memory to the base class constructor and initialise the pointer from that. That's slightly dangerous though - you must make sure you initialise the pointer immediately, before anything that might throw an exception, or the memory might leak.

class Base {
public:
    Base(int * p) : p(p) {}

private: // doesn't need to be protected now
         // (unless something else in the derived class needs access)
    boost::scoped_ptr<int> p;
};

class Derived : public Base {
public:
    Derived() : Base(new int(100)) {}
};

In C++11, you could use unique_ptr, which is movable, to avoid that risk of a leak:

class Base {
public:
    typedef std::unique_ptr<int> ptr;
    Base(ptr && p) : p(p) {}

private:
    ptr p;
};

class Derived : public Base {
public:
    Derived() : Base(ptr(new int(100))) {}
};

Upvotes: 4

Related Questions