3

I am faced with a situation, where I would like to call a virtual method from the constructor. This is of course not possible (or at least it does not produce the desired behavior). In this answer as a work-around it is proposed to use a factory method. I wrote something like this:

#include <iostream>
class Base {
    public:
        Base(){}
        ~Base(){}
    // private: ??
        virtual void afterConstruction()=0;
};
class Derived : public Base {
    public:
        Derived() : Base() {}
    //private: ??
        void afterConstruction(){std::cout<<"construct"<<std::endl;}
};
template <typename T> T MyFactory(){
    T t = T();
    T* p = &t;
    p->afterConstruction();
    return t;
}
int main(int argc, char** argv) {
    Derived d = MyFactory<Derived>();
    return 0;
}

It is kind of a template method pattern. Each derived class can customize the way it is constructed. However, this whole construct only makes sense, when a user of this classes cannot call the constructor or the afterConstruction() directly. Thus I would like to have both of them private. Maybe it is a stupid question and I just do not see the obvious. Maybe I can achieve this by using friendship or something like that, but I am not sure if this is the best way. What is a nice and clean way to hide this two methods and only allowing to create object via the factory method?

EDIT: Ka7Im1011 Made me realize that it is not really clear what I am asking for. Thus I will try to clarify:

I want to write a base class that other will have to derive from. Constructing the derived objects involves quite specific stuff that I would like to keep out of the base class. When searching the web for virtual constructor I found the above mentioned q&a and I think a factory approach could work well. However, I am not sure how to achieve the following:

  • It should only be possible to create instances of Derived from the factory. Otherwise the derived object can be created in a non-consistent state. (I guess that in the base class alone I cannot enforce this, but asking each coder of a Derived to make the constructor private/protected would suffice.)
  • If possible, Derived should only implement pure virtual methods of the Base, because then writing a Derived is quite comfortable (IDEs/compilers will tell exactly what has to be implemented as opposed to verbose and sometimes cryptic error messages when e.g. an interface of a template parameter has to be implemented)
Community
  • 1
  • 1
463035818_is_not_an_ai
  • 109,796
  • 11
  • 89
  • 185

1 Answers1

1

I Don't get your question exactly but, you might looking for this.

#include <iostream>
#include <conio.h>

class Base
{
    virtual void afterConstruction() = 0;
};
class Derived : Base {
private:
    Derived() : Base() {}
public:
     void afterConstruction(){ std::cout << "construct" << std::endl; }
protected:
    static Derived GetInstance()
    {
        return Derived();
    }
};
template <class T> class MyFactory : T
{
public:
    static T GetInstance()
    {
        // Make sure every kind of T has protected GetInstance()
        T t = T::GetInstance();
        T* p = &t;
        p->afterConstruction();
        return t;
    }
};
int main(int argc, char** argv) {
    Derived d = MyFactory<Derived>::GetInstance();
    // Derived d1; // will cause error
    _getch();
    return 0;
}

Edited Answer

#include <iostream>
#include <conio.h>

class Base
{
protected:
    Base() {  }
    virtual void afterConstruction() = 0;
    virtual Base* GetInstance() = 0;
};

class Derived : public Base {
protected:
    Derived() : Base() {  }
    void afterConstruction()
    {
        static bool bConstrucred = false;
        if (!bConstrucred)
        {
            std::cout << "construct" << std::endl;
            bConstrucred = true;
        }
    }
    Derived* GetInstance()
    {
        afterConstruction();
        return this;
    }
};

template <class T> class MyFactory : public T
{
public:
    T* GetInstance() { return T::GetInstance(); }
};

int main(int argc, char** argv) {
    Derived* d = MyFactory<Derived>().GetInstance();
    // Derived d1; // will cause error
    _getch();
    return 0;
}
Kasim Rangwala
  • 1,765
  • 2
  • 23
  • 44
  • Yes, maybe I wasnt clear enough when stating the question. What I like about the template method pattern is that the derived class only has to implement the pure virtual methods to work as expected. With this solution, each Derived class has to implement a GetInstance (which cannot be virtual because its static). But maybe I am just overcomplicating it. Anyway, doing it like this, anybody writing a derived class has no chance to create an object of this class that does not behave as expected (and the compiler will tell him what is missing in case he forgot to implement a `GetInstance`). – 463035818_is_not_an_ai Apr 02 '15 at 09:54
  • ok, first, I got your point that `Derived class can't have it's own method except virtual method of Base class`. but, I'm still confused about `Do you want to allow user to create an object of Derived class using default constructor or not?`. – Kasim Rangwala Apr 02 '15 at 10:03
  • no, this should not be possible. I will edit the question to make it more clear – 463035818_is_not_an_ai Apr 02 '15 at 10:42
  • See, My **Edited Answer**. put, comment if you're still looking for something else – Kasim Rangwala Apr 02 '15 at 10:54
  • it's my pleasure, @tobi303 – Kasim Rangwala Apr 02 '15 at 11:01
  • Just realized, that actually in the beginning I thought that also `afterConstruction` has to be private/protected to avoid a false second call to initialize the object. However, I think this I can fix by simply using a flag `isConstructed`. `afterConstruction` would then be non-virtual in the base class and call a pure virtual `doAfterConstruction` only if the flag is not set. Hope this is comment is not just adding confusion again ;) – 463035818_is_not_an_ai Apr 02 '15 at 11:08
  • No, just make `afterConstruction()` as `protected`. and just call it from `constructor` of `derived`class. `protected: Derived() : Base() { afterConstruction(); }` and remove the call inside `GetInstance()` function. and you've done. – Kasim Rangwala Apr 02 '15 at 11:16
  • I am very sorry, but I was too fast in accepting your (edited) answer. To me it looks all fine, but when I try to compile I get complaints about `Derived()` being protected when it is called in `MyFactory`. I do not understand why this produces an error, because `MyFactory` inherits from `Derived` and calling the protected constructor should be no problem. – 463035818_is_not_an_ai Apr 02 '15 at 11:18
  • In my vs-2013. it works perfectly. If you want then I can give you my visual studio solution. and by the way which compiler you're using. – Kasim Rangwala Apr 02 '15 at 11:20
  • I am using gcc 4.7.2. Concerning your proposal to call `afterConstruction` in the Derived constructor: This is something I would like to avoid (if possible) because it adds a possible mistake that can be made by someone implementing another Derived (if he forgets to call afterConstruction his class wont work as expected) – 463035818_is_not_an_ai Apr 02 '15 at 11:25
  • Thanks a lot for all your help. At the moment I am struggling with this "cannot call protected constructor" problem. But maybe I should open a different question on this. I already feel bad for starting a question that requires so much clarification and discussion. – 463035818_is_not_an_ai Apr 02 '15 at 11:45
  • I think you just need to change compiler setting. I haven't worked with GCC so, I don't know this. but you'll find it soon. don't worry and happy coding – Kasim Rangwala Apr 02 '15 at 11:56
  • According to [this](http://stackoverflow.com/questions/24636234/access-to-protected-constructor-of-base-class) calling the protected constructor is actually only allowed in the constructor initializer, thus vs would be an exception. Unfortunately I cannot rely on using a specific compiler. And even more unfortunately this makes my whole factory approach more complicated than initially expected. I will reconsider my actual use case and hope that something much simpler will do the job. Anyhow, thanks again for your effort. – 463035818_is_not_an_ai Apr 02 '15 at 12:05
  • I've edited my answer with solving your problem of `GCC protected constructor`. this will work on every compiler. Have a look – Kasim Rangwala Apr 02 '15 at 12:49