Click here to Skip to main content
11,487,979 members (77,534 online)
Click here to Skip to main content

Tagged as

Single point construction of hierarchical objects in C++

, 1 Dec 2010 CPOL 27.1K 31
Rate this:
Please Sign up or sign in to vote.
Prohibit construction of hierarchical objects both on the heap and on the stack, and allow construction from a single construction point in the base class.

Introduction

As it is well known, smart pointers with reference counting in C++ are responsible for sharing object pointers and managing their memory life cycle. When using smart pointers, it is assumed that an internal object pointer that is managed by a smart pointer is not accessible outside the smart pointer. Accessing the internal object pointer outside the smart pointer may lead to an accidental pointer deletion and subsequent memory problems. In order to avoid such problems, it would be great to allow object pointer creation only within the smart pointer and forbid its creation outside the smart pointer.

Background

In one of my projects, we built a framework that introduced a bunch of base classes that the framework works with. The framework makes use of only smart pointers created from these base classes for controlling the class object's memory life cycle. The user may extend the framework by subclassing these base classes; however, the framework utilizes only smart pointers created from these derived user classes. In order to avoid the memory problems described above, a request appeared to disallow derived classes construction on the heap (pointers) as well as on the stack (it is a 'nice to have' ability) and allow only derived class' smart pointer construction. All this has to be done by means of the framework without any assistance from the user; in other words, this should be enforced over the user derived classes.

Code overview

In this article, I show the code that allows the base class to prohibit its derived classes construction on the stack and on the heap from outside the base class and enforce all derived classes to be constructed from a single construction point within the base class.

Here is an example of the Base class that achieves the declared goal:

class Base
{
public:
    class ForbidConstruction
    {            
    private:
        ForbidConstruction()
        {}

        ForbidConstruction(const ForbidConstruction&);
        ForbidConstruction& operator=(const ForbidConstruction&);

        friend class Base;    
    };   
    
    Base(const ForbidConstruction& /* dummy parameter */)
    {
        // Do some construction activity
        ...
    } 
    
    virtual ~Base() {}

    template <typename T>
    static   SmartPtr<T> create() 
    { 
        return SmartPtr<T>( new T( Base::ForbidConstruction() ) ); 
    }

private:
    Base(const Base&);
    Base& operator=(const Base&);
};

class Derived: public Base
{
public:
    Derived(const Base::ForbidConstruction& dummy, /* other constructor parameters */) : 
    Base(dummy)
    {
        // Do some construction activity
        ...
    }
    ...
};
 
int main()
{
    SmartPtr<Derived> derived = Base::create<Derived>();
}

The class Base has a public constructor that accepts an internal class ForbidConstruction reference. Note that the internal class ForbidConstruction has a private constructor and thus can be created only within class Base that is declared as its friend. Any class that is derived from the Base class should pass the ForbidConstruction object to the Base class constructor. However, the derived class is not able to construct the ForbidConstruction object itself, so the only option is to accept the ForbidConstruction object as the derived class' constructor parameter.

Since the ForbidConstruction object can not be created anywhere outside the Base class, the Derived object can be created neither on the stack nor on the heap (pointer) outside the Base class. So the only way to construct the Derived object is from within the Base class. The Base class provides a static templated create() method that creates the Derived object pointer and returns it wrapped within a smart pointer SmartPtr class.

With the above preparations made in the Base class, we are able to create a robust framework that does not suffer from an accidental derived class pointer deletion.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Gregory Shpitalnik
Software Developer (Senior) Marvell
Israel Israel
17 years experience software engineer at Marvell company in Israel.

Comments and Discussions

 
GeneralMy vote of 5 Pin
iamxyb16-May-13 2:43
memberiamxyb16-May-13 2:43 
GeneralDerived class with constructor having parameters Pin
ck08157-Dec-10 23:48
memberck08157-Dec-10 23:48 
GeneralRe: Derived class with constructor having parameters Pin
Gregory Shpitalnik7-Dec-10 23:53
memberGregory Shpitalnik7-Dec-10 23:53 
GeneralRe: Derived class with constructor having parameters Pin
ck08158-Dec-10 3:50
memberck08158-Dec-10 3:50 
GeneralMy vote of 4 Pin
Emilio Garavaglia1-Dec-10 22:11
memberEmilio Garavaglia1-Dec-10 22:11 
GeneralRe: My vote of 4 Pin
Emilio Garavaglia1-Dec-10 22:47
memberEmilio Garavaglia1-Dec-10 22:47 
Well ... this is one of many article "out there" that tries to "fix" some C++ weakness.
What makes me wonder here are some "a priori" questions:
- Are them real weakness?
- Is the fix not creating other problem elsewhere ?

The most of OOP languages makes a clear distinction between "value types" (that exist into "scopes", may be on stack or as someone else membeer) and "reference types" (that must exist on heap). This clear dichotomy makes memory management relatively easy.
C++ doesn't: everything can be either an object or a value, and -if you have a reference- there is no way to understand which nature it has. Will tho object i'm looking at, suddenly disappear without my knowledge?

All workarounds (from reference counting, to stack-binded pointers and RAII, to single point construction) are imperfect: they solve the problem by removing feature that have their own reason to exist for other purposes.

Reference counting requires to distinguish from forward to backward pointers (to avoid loops): it eliminates the "value or reference type" dilemma, introducing another one. Is it simpler? Yes, until the context is not too wide compelling with too many interacting developers.

RAII elimitnates the dilemma at the source, by making everything behaving as if leaving on a stack. "The destroyer is the creator". But not everything in the world is like a stack: whatever application gives to a user a "delete" command must be prepared to manage object not required to exist anymore. And saying they have to continue to keep a "secret life" just because their "creator" (the GUI task) isn't dead anymore is a wasting of resource.

Even garbage collected languages -that solve the problem of managing memory- fall into the problem of "finalization of resources": should I keep a handle "open" until it exist? probably no.

In your case, you solve a problem forcing the programmer to adhere to a discipline. But what is the cost of it? How much has a programmer to type more (and repeat annoying definitions) just to avoid the danger of a new X(abcd) left alone, in a context where all API are in term of shared_ptr<X> ?!? May be the risk is not that hight to justify all that.
Just don't use yourself pointesr wherever a smart pointer is given to you by someone, and don't give to other raw pointers: just give them the appropriate smart pointer that definse the wanted destruction semantic. (shared, auto, stack, unique, copy on write, ... whatever is needed)

2 bugs found.
> recompile ...
65534 bugs found.
D'Oh! | :doh:


GeneralRe: My vote of 4 Pin
Gregory Shpitalnik1-Dec-10 23:23
memberGregory Shpitalnik1-Dec-10 23:23 
GeneralRe: My vote of 4 Pin
Arman S.6-Dec-10 8:02
memberArman S.6-Dec-10 8:02 
GeneralRe: My vote of 4 Pin
Andy Bantly14-Jun-13 12:19
memberAndy Bantly14-Jun-13 12:19 
GeneralMy vote of 5 Pin
Galatei1-Dec-10 13:52
memberGalatei1-Dec-10 13:52 
Generalcongratulations Pin
MashaMariaD1-Dec-10 9:57
memberMashaMariaD1-Dec-10 9:57 
GeneralHm... :) Pin
Eugen Podsypalnikov30-Nov-10 21:55
memberEugen Podsypalnikov30-Nov-10 21:55 
GeneralRe: Hm... :) Pin
Gregory Shpitalnik30-Nov-10 22:31
memberGregory Shpitalnik30-Nov-10 22:31 
Generalsmart pointers pointing to local variables Pin
Skond30-Nov-10 6:18
memberSkond30-Nov-10 6:18 
GeneralRe: smart pointers pointing to local variables Pin
xComaWhitex30-Nov-10 22:13
memberxComaWhitex30-Nov-10 22:13 
GeneralRe: smart pointers pointing to local variables Pin
Galatei1-Dec-10 13:48
memberGalatei1-Dec-10 13:48 
GeneralMy vote of 5 Pin
Bhasker Kandpal29-Nov-10 20:38
memberBhasker Kandpal29-Nov-10 20:38 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Terms of Use | Mobile
Web02 | 2.8.150520.1 | Last Updated 1 Dec 2010
Article Copyright 2010 by Gregory Shpitalnik
Everything else Copyright © CodeProject, 1999-2015
Layout: fixed | fluid