473,231 Members | 1,742 Online
Bytes | Software Development & Data Engineering Community
Post Job

Home Posts Topics Members FAQ

Join Bytes to post your question to a community of 473,231 software developers and data experts.

boost::shared_ptr and multiple inheritance

Suppose you have a class heirarchy as such:
class Base{
....
};
class Mid1 : public Base{
....
};

class Mid2 : public Base{
....
};
class Derived: public Mid1, Mid2{
};

Mid1, Mid2, and Base are ABC.

What is an effective way to manage the lifetime of this object?
Currently, I leak Derived (and anything similar to Derived) all over
the place by not reclaiming them at all. That works for now, in as
much as when I am done with them I am done with execution of my
program, but is not a solution that will work for the future.

My use at the moment is as such:

class ObjectHolder {
public:
....
void addMid1(Mid1 *p) { assert(p); mid1s.push_back(p); };
void addMid2(Mid2 *p) { assert(p); mid2s.push_back(p); };
private:
std::vector<Mid1*mid1s;
std::vector<Mid2*mid2s;
....
};

There will be many different classes that are equivalent to Derived
(in the sense that they are deriving from Mid1 and Mid2.) There will
also be classes that derive from one or the other of Mid1 and Mid2.
All of them will be registered exactly once in mid1s and/or mid2s,
whichever they are derived from. References to the objects will not
exist outside of ObjectHolder. Objects may contain references to
other objects; the directed graph that they form will be acyclic.

My first instinct was to delete every element of mid1s and mid2s in
the destructor for ObjectHolder, but that will not work when a Derived
has been inserted into both vectors. My next idea was to use
boost::shared_ptr and use that to automatically manage the lifetime of
the object, but there was nothing in the documentation that I saw that
led me to believe that one could use a shared_ptr across a class
heirarchy. Since the 'this' pointer for an object is different
depending on the type it is being used as, I'm (possibly erroneously)
assuming that a shared_ptr will mismanage the reference count for the
pointer since they are using different pointers. Am I wrong in my
understanding of the semantics of boost::shared_ptr?

(PS: It's obvious that what would solve my problem is a GC- I'm not
ruling it out at this point, but I am exploring my options)
Jun 27 '08 #1
4 3903
EnsGabe wrote:
Suppose you have a class heirarchy as such:
class Base{
...
};
class Mid1 : public Base{
...
};

class Mid2 : public Base{
...
};
class Derived: public Mid1, Mid2{
};

Mid1, Mid2, and Base are ABC.

What is an effective way to manage the lifetime of this object?
Currently, I leak Derived (and anything similar to Derived) all over
the place by not reclaiming them at all. That works for now, in as
much as when I am done with them I am done with execution of my
program, but is not a solution that will work for the future.

My use at the moment is as such:

class ObjectHolder {
public:
...
void addMid1(Mid1 *p) { assert(p); mid1s.push_back(p); };
void addMid2(Mid2 *p) { assert(p); mid2s.push_back(p); };
private:
std::vector<Mid1*mid1s;
std::vector<Mid2*mid2s;
...
};

There will be many different classes that are equivalent to Derived
(in the sense that they are deriving from Mid1 and Mid2.) There will
also be classes that derive from one or the other of Mid1 and Mid2.
All of them will be registered exactly once in mid1s and/or mid2s,
whichever they are derived from. References to the objects will not
exist outside of ObjectHolder. Objects may contain references to
other objects; the directed graph that they form will be acyclic.

My first instinct was to delete every element of mid1s and mid2s in
the destructor for ObjectHolder, but that will not work when a Derived
has been inserted into both vectors.
The following is based on the idea that the objects should unregister
themselves upon destruction.

#include <set>
#include <iostream>

class Owner;

class BaseA {

Owner * owner_ptr;

public:

virtual
~BaseA ( void );

void register_owner ( Owner * p );

};

class BaseB {

Owner * owner_ptr;

public:

virtual
~BaseB ( void );

void register_owner ( Owner * p );

};

class Owner {

std::set< BaseA * all_a;
std::set< BaseB * all_b;

public:

void register_a ( BaseA * p ) {
all_a.insert( p );
p->register_owner( this );
}

void register_b ( BaseB * p ) {
all_b.insert( p );
p->register_owner( this );
}

void unregister_a ( BaseA * p ) {
all_a.erase( p );
}

void unregister_b ( BaseB * p ) {
all_b.erase( p );
}

~Owner ( void ) {
while ( ! all_a.empty() ) {
std::set< BaseA * >::iterator first = all_a.begin();
BaseA * a_ptr = *first;
all_a.erase( first );
delete a_ptr;
}
while ( ! all_b.empty() ) {
std::set< BaseB * >::iterator first = all_b.begin();
BaseB * b_ptr = *first;
all_b.erase( first );
delete b_ptr;
}
}

};
BaseA::~BaseA ( void ) {
owner_ptr->unregister_a( this );
std::cout << "a";
}

void BaseA::register_owner ( Owner * p ) {
owner_ptr = p;
}
BaseB::~BaseB ( void ) {
owner_ptr->unregister_b( this );
}

void BaseB::register_owner ( Owner * p ) {
owner_ptr = p;
std::cout << "b";
}

class X : public BaseA, public BaseB {

virtual
~X ( void ) {
std::cout << "x";
}

};

int main ( void ) {
Owner o;
for ( unsigned int i = 0; i < 10; ++i ) {
X * x = new X;
o.register_a( x );
o.register_b( x );
BaseA * a = new BaseA;
o.register_a( a );
BaseB * b = new BaseB;
o.register_b( b );
}
}
My next idea was to use
boost::shared_ptr and use that to automatically manage the lifetime of
the object, but there was nothing in the documentation that I saw that
led me to believe that one could use a shared_ptr across a class
heirarchy. Since the 'this' pointer for an object is different
depending on the type it is being used as, I'm (possibly erroneously)
assuming that a shared_ptr will mismanage the reference count for the
pointer since they are using different pointers. Am I wrong in my
understanding of the semantics of boost::shared_ptr?
[snip]

If D is derived from T, then tr1::shared_ptr<Tallows assignment and copy
construction from tr1::shared_ptr<D>. So, if you do:

std::vector< shared_ptr<Mid1 mid1s;
std::vector< shared_ptr<Mid2 mid2s;

and

void addMid1 ( shared_ptr<Mid1p );
void addMid2 ( shared_ptr<Mid2p );

you can use it as follows:

shared_ptr< Derived d_ptr = new Derived ( ... );
addMid1( d_ptr );
addMid2( d_ptr );

and it should work just fine. Have you tried?

Best

Kai-Uwe Bux
Jun 27 '08 #2
On Jun 22, 4:11*am, Kai-Uwe Bux <jkherci...@gmx.netwrote:
The following is based on the idea that the objects should unregister
themselves upon destruction.

#include <set>
#include <iostream>

class Owner;

class BaseA {

* Owner * owner_ptr;

public:

* virtual
* ~BaseA ( void );

* void register_owner ( Owner * p );

};

class BaseB {

* Owner * owner_ptr;

public:

* virtual
* ~BaseB ( void );

* void register_owner ( Owner * p );

};

class Owner {

* std::set< BaseA * all_a;
* std::set< BaseB * all_b;

public:

* void register_a ( BaseA * p ) {
* * all_a.insert( p );
* * p->register_owner( this );
* }

* void register_b ( BaseB * p ) {
* * all_b.insert( p );
* * p->register_owner( this );
* }

* void unregister_a ( BaseA * p ) {
* * all_a.erase( p );
* }

* void unregister_b ( BaseB * p ) {
* * all_b.erase( p );
* }

* ~Owner ( void ) {
* * while ( ! all_a.empty() ) {
* * * std::set< BaseA * >::iterator first = all_a.begin();
* * * BaseA * a_ptr = *first;
* * * all_a.erase( first );
* * * delete a_ptr;
* * }
* * while ( ! all_b.empty() ) {
* * * std::set< BaseB * >::iterator first = all_b.begin();
* * * BaseB * b_ptr = *first;
* * * all_b.erase( first );
* * * delete b_ptr;
* * }
* }

};

BaseA::~BaseA ( void ) {
* owner_ptr->unregister_a( this );
* std::cout << "a";

}

void BaseA::register_owner ( Owner * p ) {
* owner_ptr = p;

}

BaseB::~BaseB ( void ) {
* owner_ptr->unregister_b( this );

}

void BaseB::register_owner ( Owner * p ) {
* owner_ptr = p;
* std::cout << "b";

}

class X : public BaseA, public BaseB {

* virtual
* ~X ( void ) {
* * std::cout << "x";
* }

};

int main ( void ) {
* Owner o;
* for ( unsigned int i = 0; i < 10; ++i ) {
* * X * x = new X;
* * o.register_a( x );
* * o.register_b( x );
* * BaseA * a = new BaseA;
* * o.register_a( a );
* * BaseB * b = new BaseB;
* * o.register_b( b );
* }

}
Interesting. This couples the classes together, though. I'd like to
avoid that.

>
If D is derived from T, then tr1::shared_ptr<Tallows assignment and copy
construction from tr1::shared_ptr<D>. So, if you do:

* std::vector< shared_ptr<Mid1 mid1s;
* std::vector< shared_ptr<Mid2 mid2s;

and

* void addMid1 ( shared_ptr<Mid1p );
* void addMid2 ( shared_ptr<Mid2p );

you can use it as follows:

* shared_ptr< Derived d_ptr = new Derived ( ... );
* addMid1( d_ptr );
* addMid2( d_ptr );

and it should work just fine. Have you tried?

Best

Kai-Uwe Bux
I've tried it, and it compiles, but I learned long ago that is a far
cry from saying that something is working. Can you point me in the
direction of documentation for this?
Jun 27 '08 #3
EnsGabe wrote:

[snip]
>>
If D is derived from T, then tr1::shared_ptr<Tallows assignment and
copy construction from tr1::shared_ptr<D>. So, if you do:

std::vector< shared_ptr<Mid1 mid1s;
std::vector< shared_ptr<Mid2 mid2s;

and

void addMid1 ( shared_ptr<Mid1p );
void addMid2 ( shared_ptr<Mid2p );

you can use it as follows:

shared_ptr< Derived d_ptr = new Derived ( ... );
addMid1( d_ptr );
addMid2( d_ptr );

and it should work just fine. Have you tried?

Best

Kai-Uwe Bux

I've tried it, and it compiles, but I learned long ago that is a far
cry from saying that something is working. Can you point me in the
direction of documentation for this?
I am looking at the draft n2521 for C++0X. There, you will find what you are
looking for in clause [20.6.6.2.1/19-22]:

shared_ptr(shared_ptr const& r);
template<class Yshared_ptr(shared_ptr<Yconst& r);
Requires: For the second constructor Y* shall be convertible to T*.
Effects: If r is empty, constructs an empty shared_ptr object; otherwise,
constructs a shared_ptr object that shares ownership with r.
Postconditions: get() == r.get() && use_count() == r.use_count().
Throws: nothing.
...

Note the postconditions.

Assignment is dealt with in [20.6.6.2.3]. Whichever version of shared_ptr<>
you are using, look up the documentation of copy-construction and
assignment.

Best

Kai-Uwe Bux
Jun 27 '08 #4
On Jun 22, 8:23 am, EnsGabe <ensg...@gmail.comwrote:
Suppose you have a class heirarchy as such:
class Base{
...
};
class Mid1 : public Base{
...
};
class Mid2 : public Base{
...
};
class Derived: public Mid1, Mid2{
};
Mid1, Mid2, and Base are ABC.
Are you sure that you want several instances of Base in the
final object? Or do you want virtual inheritance.
What is an effective way to manage the lifetime of this object?
In what way? How you manage lifetime of an object depends on
the semantics of the object.
Currently, I leak Derived (and anything similar to Derived)
all over the place by not reclaiming them at all.
You mean you never call delete on them. (If the objects don't
have a deterministic lifetime, requiring a explicit call to
delete, then garbage collection is the obvious answer.)
That works for now, in as much as when I am done with them I
am done with execution of my program, but is not a solution
that will work for the future.
My use at the moment is as such:
class ObjectHolder {
public:
...
void addMid1(Mid1 *p) { assert(p); mid1s.push_back(p); };
void addMid2(Mid2 *p) { assert(p); mid2s.push_back(p); };
private:
std::vector<Mid1*mid1s;
std::vector<Mid2*mid2s;
...
};
There will be many different classes that are equivalent to
Derived (in the sense that they are deriving from Mid1 and
Mid2.) There will also be classes that derive from one or the
other of Mid1 and Mid2. All of them will be registered
exactly once in mid1s and/or mid2s, whichever they are derived
from. References to the objects will not exist outside of
ObjectHolder. Objects may contain references to other
objects; the directed graph that they form will be acyclic.
In which case, reference counted pointers can be used as a
substitute for garbage collection. It's more invasive, and
usually somewhat slower, but installing boost::shared_ptr is a
lot easier than installing the Boehm collector.
My first instinct was to delete every element of mid1s and
mid2s in the destructor for ObjectHolder, but that will not
work when a Derived has been inserted into both vectors. My
next idea was to use boost::shared_ptr and use that to
automatically manage the lifetime of the object, but there was
nothing in the documentation that I saw that led me to believe
that one could use a shared_ptr across a class heirarchy.
You can, provided that all of the instances of shared_ptr derive
from the same initial shared_ptr. (What you can't do is create
an initial shared_ptr twice from a raw pointer. Basically,
you should probably create the initial shared_ptr as a
shared_ptr< Derived >, at the site of the new, and use nothing
but shared_ptr after that---shared_ptr supports all of the usual
pointer conversions, or it should.)
Since the 'this' pointer for an object is different
depending on the type it is being used as, I'm (possibly erroneously)
assuming that a shared_ptr will mismanage the reference count for the
pointer since they are using different pointers. Am I wrong in my
understanding of the semantics of boost::shared_ptr?
Sort of. Globally, there are two large families of reference
counted pointers: invasive and non-invasive. Since you're
dealing with a known hierarchy, you can easily use either. The
invasive pointers have the advantage that you can create new
smart pointers from the raw pointer anywhere you want; you
could, for example, have the only smart pointers in the two
containers, and that would work. On the other hand, when
multiple inheritance is involved, the base class (which derived
from the RefCntObj, or whatever) *must* be virtual, period.
Non-invasive pointers have the advantage that the pointed to
object doesn't need to be aware that reference counted pointers
are being used---you can even create a shared_ptr<int(not that
there would ever be any reason to). On the other hand, every
time you create an instance of the smart pointer from a raw
pointer, you get a new counter.

Which is more appropriate in your case depends on the way the
objects are allocated and who manages the containers.

--
James Kanze (GABI Software) email:ja*********@gmail.com
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
Jun 27 '08 #5

This thread has been closed and replies have been disabled. Please start a new discussion.

Similar topics

4
by: Philippe Guglielmetti | last post by:
I just ported old (VC6) working code to VC7.1 and have trouble with something like: class A; // forward typedef boost::smart_ptr<A> Aptr; class B{ Aptr a; virtual ~B(); // implemented...
5
by: ctick | last post by:
Are there any advantages of using boost::shared_ptr other than auto_ptr from standard library?
6
by: Ryan Mitchley | last post by:
Hi all Given bool bResult; shared_ptr<cSampleData> pNewData; shared_ptr<cBase> pNewBase; where cSampleData is descended from cBase, the following gives me a valid pNewData to the correct...
2
by: krema2ren | last post by:
Hi I've the following header problem that I need two classes to know each other through a boost::shared_ptr. Does any of you smart guys have a solution? A.h ---------------------- #include...
2
by: adebaene | last post by:
Hello group, There seems to be a bug int the interop layer in VC2005 when dealing with certain pointer types (or values?) Here is a repro case using Boost version 1.32 and C++/CLI : using...
6
by: Toby Bradshaw | last post by:
Hi, Consider the following: class A { public: virtual bool foo() = 0; };
2
by: ketan | last post by:
Hi, While reading the Boost::shared_ptr implementation, I came across this method template<class Tclass shared_ptr { ..... template<class Y> shared_ptr(shared_ptr<Yconst & r,...
1
by: rssmps | last post by:
Hi, first post and here it goes... This is the current code (working) that I have. It works but for large number of A & B objects, it's not the most efficient. It's also not really heterogenous...
10
by: tradevol | last post by:
Hi, I am playing with boost pointer and try to wrap the following codes A* func(){ ... if(condition 1 ){ return a; } else
0
by: VivesProcSPL | last post by:
Obviously, one of the original purposes of SQL is to make data query processing easy. The language uses many English-like terms and syntax in an effort to make it easy to learn, particularly for...
3
isladogs
by: isladogs | last post by:
The next Access Europe meeting will be on Wednesday 3 Jan 2024 starting at 18:00 UK time (6PM UTC) and finishing at about 19:15 (7.15PM). For other local times, please check World Time Buddy In...
0
by: jianzs | last post by:
Introduction Cloud-native applications are conventionally identified as those designed and nurtured on cloud infrastructure. Such applications, rooted in cloud technologies, skillfully benefit from...
2
isladogs
by: isladogs | last post by:
The next Access Europe meeting will be on Wednesday 7 Feb 2024 starting at 18:00 UK time (6PM UTC) and finishing at about 19:30 (7.30PM). In this month's session, the creator of the excellent VBE...
0
by: fareedcanada | last post by:
Hello I am trying to split number on their count. suppose i have 121314151617 (12cnt) then number should be split like 12,13,14,15,16,17 and if 11314151617 (11cnt) then should be split like...
0
by: stefan129 | last post by:
Hey forum members, I'm exploring options for SSL certificates for multiple domains. Has anyone had experience with multi-domain SSL certificates? Any recommendations on reliable providers or specific...
0
Git
by: egorbl4 | last post by:
Скачал я git, хотел начать настройку, а там вылезло вот это Что это? Что мне с этим делать? ...
0
by: MeoLessi9 | last post by:
I have VirtualBox installed on Windows 11 and now I would like to install Kali on a virtual machine. However, on the official website, I see two options: "Installer images" and "Virtual machines"....
0
by: DolphinDB | last post by:
Tired of spending countless mintues downsampling your data? Look no further! In this article, you’ll learn how to efficiently downsample 6.48 billion high-frequency records to 61 million...

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.