C++: How to manage object lifetimes and dependencies?
A concrete problem:
I have a Main application which has objects of type A and type B (among other types). Object of type B requires A object to be properly constructed (so there is a constructor A(const B& b). However Main may change B object it holds at any time. How do I make sure that when Main changes its B object then the A object's internal reference is changed ?
In general, what are some good practices to manage object lifetimes, where obje开发者_StackOverflowcts have dependencies ?
If A
never caches any of B
properties, and always references the instance of B
it holds to generate any dependent output, any changes that are made to B
should be reflected in subsequent calls to A
. I am assuming you're simply storing a reference to B
within the constructor and not creating a local copy.
If I understand correctly, you want to not just change the B object but completely replace it with a different B. References can't be changed once created, so you'll want to use pointers instead.
You may want to use the Observer Pattern to let the A objects know when their B should be replaced: http://en.wikipedia.org/wiki/Observer_pattern
In general: Always make sure you know about the ownership. Whenever you create an object, wither another object needs to be the owner or it has to be a local variable. In your case the main routine would be the owner of the instance to B. If you have a reference to B in your A instance, A will see all changes to the instance - just make sure you do not copy (not having a reference does implicit copying). So in your code you would have something like
private:
const B& theReference;
or
private:
B& theReference;
if you need to call non-const methods (remember to also change your constructor in that case).
If I understood you correctly, if you make modifications to an object that main holds, it should in turn effect the object what A
holds. For this you may take the help of constructor initializer.
#include <iostream>
class B{
public:
int num ;
B(int arg):num(arg) {}
};
class A{
public:
const B& ref ;
A( const B& arg ): ref(arg){}
};
int main()
{
B objOne(10) ;
A objTwo(objOne) ;
std::cout << objTwo.ref.num << std::endl ;
objOne.num = 20 ;
std::cout << objTwo.ref.num << std::endl ;
}
Output :
10
20
Keep in mind:
- All problems can be solved with one more layer of indirection.
- Object ownership must be obvious.
In your case, if the B
instance can come-and-go at any time (the old instance is deleted, a new one is "newed"), then you can create a "utility handle" class that "wraps" the B
instance:
class BHandle {
B* b_; // can change at any time
public:
....
};
Then, your A
class would reference a BHandle
instance, or wholly contain a BHandle
instance. Then, B
instances can come-and-go, but A::my_b_handle_
would always reflect where the "current" B
instance is.
On the other hand, if the B
instance merely has data members that change (its instance itself does not come-and-go), then you don't need to do anything (A
will always reference the same B
instance, and you may in some cases merely need to "notify" A
that properties changed in the B
object it references).
Here's how I handled the problem. User code looks like this:
class Env
{
public:
Env();
~Env();
private:
void *priv;
};
class MyInterface
{
public:
MyInterface(Env &e) : e(e) { }
int create_A();
void use_A(int a);
private:
Env &e;
void *priv;
};
int main()
{
Env e;
MyInterface i(e);
int a = i.create_A();
use_A(a);
}
This way every dependency is visible in the user code. The dependencies between objects are nicely stored inside a std::vectors in a Env class. Indexes to the vectors will be returned from the functions. create_A() and use_A() can communicate via ints. The objects will all be destroyed at the same time when Env class goes out of the scope. Your objects could be deriving from a base class which has virtual destructor.
If you have more than one int, recommended way is this:
struct ID { int i; };
Implementation of the interface would rely on the following functions:
A *find_a(const Env &e, ID i);
ID create_a(Env &e, A *ptr);
The above approach solves the following problems with object lifetimes:
- lifetime of the objects
- dependencies between the objects (via ints)
- identifying the objects
- the dependencies could be stored either via int's or via pointers
- destroying the objects when lifetime ends
精彩评论