Suppose structs A and B are singleton structs defined as follows:
struct A
{
B& b_;
static A& shared_a() { ... }
A() : b_(B::shared_b()) { ... }
};
struct B
{
A& a_;
static B& shared_b() { ... }
B() : a_(A::shared_a()) { ... }
};
Suppose that the file structure is organized so that the code will compile.
The first time A::shared_a is called, it will construct the shared instance of A. The constructor for the shared instance of A will call B::shared_b, which will construct the shared instance of B. Next, the constructor for the shared instance of B will call A::shared_a. However, the shared instance of A has not finished its constructor! Therefore, these constructors will loop infinitely.
In order to prevent such a loop, I could merge classes A and B, but I would like to avoid doing so. Is there a more elegant solution?
Thanks,
Sam
Your code exhibits undefined behavior, and you might get either the infinite loop that you mention or any other weird behavior. Now, the problem is not with how to solve it, but rather break the cyclic dependency, which is usually a code smell.
If you are still convinced that your design makes sense, and if your constructors only store the references (without using the objects) you can change the constructors to take a reference to the object.
Again, I would avoid the cyclic dependency.
How about you give the shared_a() a reference of B itself so in the A_constructor of shared_a it can set that reference to be its b_?
Related
I'm new to C++, with a C# background. I'm trying to use dependency injection commonly used in C# in C++ and I'm confused about the different ways to declare dependencies and pass them in and why I would use one over the other. Let's assume A depends on B.
Approach #1 - declare as object and take object
class A
{
private:
B _b;
public:
A(B b): _b(b) { }
void Foo()
{
_b.Bar();
}
}
Approach #2 - declare as object and take reference
class A
{
private:
B _b;
public:
A(B &b): _b(b) { }
void Foo()
{
_b.Bar();
}
}
Approach #3 - declare as reference and take object - scratch this - undefined behavior
class A
{
private:
B &_b;
public:
A(B b): _b(b) { }
void Foo()
{
_b.Bar();
}
}
Approach #4 - declare as reference and take reference
class A
{
private:
B &_b;
public:
A(B &b): _b(b) { }
void Foo()
{
_b.Bar();
}
}
I've tried all of the above and they all seem to work. Is there any difference between them? and if so why should I choose one over the other? my preference is #1 because I find it the most familiar and intuitive.
I've found a similar discussion discussion here but that's more about Pointers vs References.
One of the main difference between C# and C++ in this regard is that in C# classes are reference types whereas in C++ they are value types.
Approach 1 and 2 are based on copy construction. This could work if the object passed to the constructor is exactly a B. But it might fails due to object slicing if you'd use a derived class of B. Approach 2 might also fail if the source object is a constant.
Approach 3: no longer considered (scratched in your question)
Approach 4: is ok, provided the object passed by reference continues to exist as long as the object. However if function B::Bar() is not a virtual function, B::Bar() would always be called in Foo(), even if you'd use a class derived from B with its own implementation of Bar().
Approach 5: use a smart pointer and clone the object passed by reference (either with a clone() member, or by using templated constructor):
class A
{
private:
unique_ptr<B> _b;
public:
template<class T>A(const T&b): _b(make_unique<T>(b)) { }
void Foo()
{
_b->Bar();
}
};
Online demo
In your case I'd say that best solution from all mentioned is taking and storing reference. The whole point of dependency injection is using pure virtual interface without even knowing how something is implemented. It means that underneath you want vtable lookup that will execute whatever implementation is for object that is referenced. This way you can expect reference for Animal where callers will provide Elephant or Cat implementation of Animal. Moreover, if you use pure virtual interface (with at least one pure virtual function inside) you can't even pass object by value because you can't create object of pure virtual type.
As far as I remmember, C# similarly to Java distinguishes objects from builtin types and objects are passed by reference by default. In C/C++ you explicitly pass everything the way you want, so in your cases you have:
Copy of temporary copy of B You make more copies than you need. If your object is big, it will cause performance loss.
Copy of B If you simply want to store copy of some object it seems to be better way but you created not needed limitation here. If you make copy of an object, you won't modify original object. By passing it by non-const reference you create this limitation and you can't take const objects because of that. If you simply want to take some object by reference and store copy of it, most of the time you want const reference. Exceptions might be in case of noncopyable objects.
reference to temporary copy of B (invalid)
Reference to B Is completely different story. Here you don't store copy of an object but point to original one, so every change you make on this object will be visible to anyone else that has access to object you received. That's more or less how objects work by default in languages like Java. Moreover, you can use virtualization as I mentioned before. The only drawback is that you have to ensure that this object exists as long as you are using it - you are not the owner of B. You just use that but someone has control of it's existence.
As you can see, in first case Second case creates some not needed limitation. You create copy of non-const
There's some code at my company that takes the following form:
class ObjectA {
public:
ObjectA(ObjectB &objectB): objectB(objectB) { }
void Initialize() { ... }
private:
ObjectB &objectB;
};
class ObjectB {
public:
ObjectB(ObjectA &objectA): objectA(objectA) { }
void Initialize() { ... }
private:
ObjectA &objectA;
};
Basically, two classes have a mutual dependency on the other.
This in itself isn't what bothers me, (although it's not great design IMO), it's that the mutual dependencies are passed through the constructor by reference in both situations.
So, the only way to actually construct this pair of objects (they are both required in the application) is to pass an incomplete reference as a dependency. To account for this, the separate Initialize methods are added as a 2nd stage constructor which are then called after both objects are created. Consequently, the constructors often times do nothing except assign the constructor parameters to the object internals, and initialize everything in the Intizialize method.
Although the code may be valid, my inclination is that this is a fundamentally flawed design for the following reasons:
The state of a dependent object can't be guaranteed in the
constructor
It requires the developers to make an additional call to Initialize
It precludes the use of compiler warnings that check if member variables have been initialized
There's nothing preventing Initialize from being called multiple times, which can result in strange, hard to track down errors
My coworkers tell me that having a separate initialize method simplifies the object construction since the developer doesn't have to worry about what order objects are declared in the application root. They can be declared and constructed in completely arbitrary order since everything is guaranteed to be valid once Initialize gets called.
I've been advocating that object construction should follow the same order of the dependency graph and that these objects should be redesigned to remove the mutual dependency, but I wanted to see what the SO gurus had to say about it. Am I wrong to think this is bad practice?
Edit:
The way these classes actually get constructed is through a factory class like below:
Factory {
public:
Factory():
objectA(objectB),
objectB(objectA) {
}
private:
ObjectA objectA;
ObjectB objectB;
};
This is bad practice yes. Passing a reference to objectB for objectA to work with while it's not properly initialized yet is a no-no.
It might be standard compliant and safe code now because ObjectA doesn't try to access the passed reference to ObjectB in its constructor but that safety is hanging by a thread. If later on someone decides to just initialize in the constructor or access anything from objectB, or change it any other way where objectB will be used you end up with UB.
This sounds like a use case for pointers that are reseated after both constructors have run.
I too don't like the code as posted - it is unnecessarily complicated and fragile. There is usually some concept of ownership when two classes cooperate like this, so, if objects of type A own objects of type B (which they probably should), then you would do something like:
#include <memory>
class A;
class B
{
public:
B (A& a) : a (a) { }
private:
A& a;
};
class A
{
public:
A () { b = std::make_unique <B> (*this); }
private:
std::unique_ptr <B> b;
};
Live demo
I have the following classes
class a {
std::shared_ptr<b> b_ref;
public:
a(std::shared_ptr<b> b_ref) : b_ref(b_ref) {}
};
class b {
std::shared_ptr<a> a_ref;
public:
b(std::shared_ptr<a> a_ref) : a_ref(a_ref) {}
};
Now I want to create two objects that reference each other is there any way to do this? I think with c-style pointers I could achieve this. Does it work with shared pointers as well? I tried the reset() and swap() methods without any success.
std::shared_ptr<a> a_ref;
std::shared_ptr<b> b_ref;
a_ref = std::make_shared<a>(b_ref);
b_ref = std::make_shared<b>(a_ref);
Did not work.
Create one object first. Then the other. Set any pointers that haven't been set. That's it. Note that these circularly referring objects won't be freed automatically when there are no more external references to them: the cyclic dependency needs to be broken very explicitly.
Where there are circular dependencies, direct use of smart pointers is usually not an optimal solution. Do consider other designs.
It is easy to see that the problem as stated has no solution, as you can only create one of one the class instance at a time.
I have two classes A and B such that A has a static B instance as its member. B has a function Show() and here is my class A:
class A
{
A()
{
_b.Show();
}
private:
static B _b;
};
and subsequent code is
A a;
B A::_b;
int main()
{
}
Now B::Show() called before B is constructed due to the sequence in which I have defined
a and _b. But how does this work exactly i.e. how is it possible to make calls on an object that is still not constructed ?
It's not possible, it's undefined behavior (in this case, because you're accessing an uninitialized object) because a is initialized before A::_b.
Look up static initialization order fiasco. You don't get an error because 99% of the times this happens, it's not easily diagnosable.
This is the static initialisation order problem. Hard to solve in the general case, but one way to fix some cases is to put the dependency inside a function like this:
class A
{
public:
A()
{
getB().Show();
}
private:
static B& getB()
{
static B b;
return b;
}
};
statics within functions are guaranteed to be initialised the first time that a call is made to it. Additionally in C++11 this is guaranteed to be thread safe as well.
Further reading:
http://www.parashift.com/c++-faq/static-init-order-on-first-use.html
Finding C++ static initialization order problems
how is it possible to make calls on an object that is still not
constructed?
Well, the simple answer is, exactly the way that code does it. <g>. Objects defined at file scope in the same translation unit are constructed in the order in which their definitions occur, so if the constructor of one object relies on another object, you have to ensure that the second object has already been constructed when the constructor for the first object runs.
C++ doesn't offer any help here; you have to keep track of it yourself.
Is there any way to declare these classes in a header file without indirection?
// Forwards declaration of B
class B;
class A
{
public:
// Default parameter referring to B. May return its parameter
const B& func(const B& b = B());
};
class B
{
public:
// B ctors
B() {}
B(const B&) {}
// B has A as a member
A a;
};
Visual C++ 2008 tells me with this:
error C2514: 'B' : class has no constructors
and points to the forward declaration of B ("class B;") and obviously can't see B's constructors below. A can't follow B because B contains A as a member.
If indirection must be used, what's the best way? Perhaps in C++0x B's A could be a unique_ptr member? Or maybe there's a boost class purely to sidestep this issue?
Instead of a default parameter declare two overloads, one that takes a B by reference and one that takes no parameter. In the one that takes no parameter, call the other with B(), which will work because that method can be defined after B is defined.
...
void func();
void func(const B& b);
};
class B...
void A::func() { func(B()); }
void A::func(const B&) { }
Update:
func() returns a const B&...
That's probably not a good idea. With that definition, something like:
const B& foo = a.func();
foo.bar();
would cause the dreaded "undefined behavior" (i.e., crash) because the B to which you have a reference will be destroyed as soon as the first statement is complete. Returning things other than class members by reference is usually a bad idea.
If you really want to do this, then I think you're stuck with forcing the caller to explicitly pass in B(), that is have no default parameter.
a.func(B()).bar();
(This is the only way to avoid undefined behavior with such a function.)
Of course you could just return a copy instead of a reference, but I presume you have a reason for not doing that.
Depending on what you're doing you may be able to set up better semantics using a smart pointer like shared_ptr instead of references so that you can effectively ignore the lifetimes of the objects. You then have to start being careful of reference cycles instead, however.
I can't tell what you're trying to use this for, but you might want to have a look at some Design Patterns to see if there is an established best-practice for it. You may find that this little problem is a symptom of an unfortunate choice of class structure or containment.