Wednesday 22 June 2016

C++ - Restrict Creation of Objects

1. Problem Description
This is a Microsoft interview question from careercup. Here is the description of the original thread,
"
how to restrict creation of object inside the function fun 
although destructor and constructor is private??
#include <iostream>

class ABC
{
private :
~ABC()
{

}

ABC()
{
std::cout <<"ABC";
}


public:
static void fun()
{


ABC t;

}



};
int main()
{
ABC::fun();

}
- jkl June 19, 2016 in India | Report Duplicate | Flag ".

2. Scope
This is a very vague question. Here I am going to concentrate on the process of the object creation and the object deletion. The first stage is compiler has to know all the data types of the class before creating an object out of it. It includes all its own member variables and the data members of its base class(es). The second stage is the order of object creation and object deletion. It starts from the member variables of its base classes to the constructor(s) of the base class then in the order of hierarchy of inheritance to its own member variables and therefore its own constructor. The object deletion is the exactly the opposite way of creation. The solutions will be focusing on the chain of these two stages.

3. Template
This solution is on the first stage. The types of the class has to be resolved before object creation. Any ambiguity will stop creating objects.

template <typename T>
class Foo
{
    Foo() {
        std::cout << "Foo()" << std::endl;
    }
    ~Foo() {
        std::cout << "~Foo()" << std::endl;
    }
public:
    static void Bar() {
        Foo f;
    }
};

Templatized this class stops creating objects because "Foo f;" does not provide any information for compiler to deduce template type and generate the instance of class.

4. Member Variables 
As the code only provides default constructor, any member variables that cannot be initialized/created via its default constructor will stop creating Foo object. This means that all types of its member variables including the member variables of its base class(es) have to provide default constructors.

class Foo
{
    int &m_Val;
    Foo() {
        std::cout << "Foo()" << std::endl;
    }
    ~Foo() {
        std::cout << "~Foo()" << std::endl;
    }
public:
    static void Bar() {
        Foo f;
    }
};

Reference type does not provide default constructor and it has to be initialized via existing variables in the initialization list of constructor. m_Val will stop creating Foo objects.

class Abc
{
public:
    Abc(int x)
    {}
};
class Foo 
{
    Abc m_Abc;
    Foo() {
        std::cout << "Foo()" << std::endl;
    }
    ~Foo() {
        std::cout << "~Foo()" << std::endl;
    }
public:
    static void Bar() {
        Foo f;
    }
};

The same reason m_Abc can't be initialized because class Abc does not provide a default constructor. The fix will have to provide a default constructor of Abc or initialize m_Abc in Foo's initialization list.

Any member variables like above m_Abc appearing in Foo's base class which has no default constructor implementation will propagate the creation failure to Foo object.

5. Access Control
Here it refers to the constructor and destructor access control of its base class, which have to be accessible in object creation and deletion of derived class. In Foo::Bar() function it can access Foo's constructor and destructor because Bar() is a member function of Foo even though Foo's constructor and destructor are declared private. But if Foo has base class(es), hiding the access of either constructor or destructor of its base class(es) will stop Foo object creation.

class Interface
{
    Interface();
    ~Interface();
};


class Foo : public Interface
{
    Foo() {
        std::cout << "Foo()" << std::endl;
    }
    ~Foo() {
        std::cout << "~Foo()" << std::endl;
    }
public:
    static void Bar() {
        Foo f;
    }
};

Declaring either constructor or destructor of Interface will stop creating Foo objects, because Foo cannot access its bases classes private section.

6. Constructor Signature Mismatch
If there is no default constructor is implemented in base classes, then the constructor of base class(es) has to be specifically appearing in initialization list in order to be called. Otherwise it breaks the object creation process. Simply compiler does not how to initialize member variables or status in generally of its base class(es).

class Abc
{
public:
    Abc(int x)
    {}
};
class Foo : public Abc
{
    Foo() {
        std::cout << "Foo()" << std::endl;
    }
    ~Foo() {
        std::cout << "~Foo()" << std::endl;
    }
public:
    static void Bar() {
        Foo f;
    }
};

The fix is to call Abc's constructor in the initialization list of Foo.

7. Abstract Classes
The object of absctract classes cannot be created because compiler has no where to populate the virtual table. Any derived class that does not implement all abstract virtual function will remain as abstract classes and therefore cannot create objects out of it.
 
class Foo
{
    Foo() {
        std::cout << "Foo()" << std::endl;
    }
    ~Foo() {
        std::cout << "~Foo()" << std::endl;
    }
public:
    static void Bar() {
        Foo f;
    }

    virtual void PrintSelf() = 0;
};

Foo has an abstract virtual function PrintSelf() and therefore cannot create objects out it.

class Interface
{
    virtual int GetVersion() = 0;
};
class Foo : public Interface
{
    Foo() {
        std::cout << "Foo()" << std::endl;
    }
    ~Foo() {
        std::cout << "~Foo()" << std::endl;
    }
public:
    static void Bar() {
        Foo f;
    }
};


Foo inherits from an abstract class Interface but does not implement the abstract virtual function. Therefore it remains as an abstract function and cannot create objects out of it.

No comments:

Post a Comment