Using links as classes for dependencies

I return to C ++ after spending some time in memory-driven languages, and suddenly get lost in what is the best way to implement dependency injection. (I am completely sold by DI because I found that this is the easiest way to make the test driven design very simple).

Now browsing SO and Google has brought me quite a few opinions on this, and I'm a little confused.

In response to this question, Dependency injection in C ++ , someone suggested you not pass raw pointers even for dependency injection. I understand that this is related to ownership of objects.

Now, ownership of objects is also discussed (although not in sufficient detail in my condition;)) in the infamous Google style guide: http://google-styleguide.googlecode.com/svn/trunk/cppguide.xml#Smart_Pointers

So I understand that in order to clarify which object owns some other objects, you should avoid passing raw pointers. In particular, it seems that against this kind of encoding:

class Addict { // Something I depend on (hence, the Addict name. sorry.) Dependency * dependency_; public: Addict(Dependency * dependency) : dependency_(dependency) { } ~Addict() { // Do NOT release dependency_, since it was injected and you don't own it ! } void some_method() { dependency_->do_something(); } // ... whatever ... }; 

If Dependency is a purely virtual class (called the Poor Interface), then this code makes it easier to implement a dummy version of Dependency (using something like google mock).

The problem is that I really don't see the problems that I may encounter with such code, and why should I want to use something else besides raw pointers! Is it not clear where the addiction came from?

In addition, I read quite a few posts hinting that links should really be used in this situation, so is this code better?

 class Addict { // Something I depend on (hence, the Addict name. sorry.) const Dependency & dependency_; public: Addict(const Dependency & dependency) : dependency_(dependency) { } ~Addict() { // Do NOT release dependency_, since it was injected and you don't own it ! } void some_method() { dependency_.do_something(); } // ... whatever ... }; 

But then I get other equally authoritative tips against using links as a member: http://billharlan.com/pub/papers/Managing_Cpp_Objects.html

As you can see, I'm not quite sure about the relative pros and cons of the various approaches, so I'm a little confused. I am sorry if this was discussed until death, or if it is only a matter of personal choice and consistency within this project ... but any idea is welcome.




Summary of responses

(I donโ€™t know if this is a good SO-ticket for this, but I will add an example code for what I have compiled from the answers ...)

Of the various answers, here is what I will probably end up doing in my case:

  • pass dependencies as a reference (at least to make sure NULL is not possible)
  • in the general case, when copying is not possible, explicitly prohibit it and save the dependencies as a link
  • in the rarer case when copying is possible, save the dependencies as RAW pointers
  • let the creator of dependencies (of some factory) choose between stack allocation or dynamic allocation (and in the latter case, control via a smart pointer)
  • establish an agreement on the separation of dependencies from own resources

So, I would like to get something like:

 class NonCopyableAddict { Dependency & dep_dependency_; // Prevent copying NonCopyableAddict & operator = (const NonCopyableAddict & other) {} NonCopyableAddict(const NonCopyableAddict & other) {} public: NonCopyableAddict(Dependency & dependency) : dep_dependency_(dep_dependency) { } ~NonCopyableAddict() { // No risk to try and delete the reference to dep_dependency_ ;) } //... void do_some_stuff() { dep_dependency_.some_function(); } }; 

And for the copyable class:

 class CopyableAddict { Dependency * dep_dependency_; public: // Prevent copying CopyableAddict & operator = (const CopyableAddict & other) { // Do whatever makes sense ... or let the default operator work ? } CopyableAddict(const CopyableAddict & other) { // Do whatever makes sense ... } CopyableAddict(Dependency & dependency) : dep_dependency_(&dep_dependency) { } ~CopyableAddict() { // You might be tempted to delete the pointer, but its name starts with dep_, // so by convention you know it is not your job } //... void do_some_stuff() { dep_dependency_->some_function(); } }; 

From what I understand, there is no way to express the intention โ€œI have a pointer to some things, but I donโ€™t own it,โ€ which the compiler can use. So I have to resort to a naming convention here ...




Stored for reference.

As Martin pointed out, the following example does not solve the problem.

Or, if I have a copy constructor, something like:

 class Addict { Dependency dependency_; public: Addict(const Dependency & dependency) : dependency_(dependency) { } ~Addict() { // Do NOT release dependency_, since it was injected and you don't own it ! } void some_method() { dependency_.do_something(); } // ... whatever ... }; 
+24
c ++ dependency-injection
Dec 29 '09 at 13:39
source share
7 answers

There is no hard and fast rule:
Since people mentioned the use of links inside objects, there may be problems with the copy (and this is so), therefore it is not a panacea, but it can be useful for a certain situation (therefore C ++ gives us the opportunity to do all this in different ways). But using RAW pointers is actually not an option. If you dynamically highlight objects, you should always support them with smart pointers, and your object should also use smart pointers.

For people who require examples: streams are always transmitted and saved as links (since they cannot be copied).

Some comments on code examples:

Example one and two

Your first example with pointers. It is basically the same as the second example using links. The difference is that the link cannot be NULL. When you pass the link, the object is already alive and, therefore, should have a longer lifespan than the object that you are already testing (if it was created on the stack), so it should be safe to store the link. If you dynamically create pointers as dependencies, I would consider using boost :: shared_pointer or std :: auto_ptr depending on whether the dependency is shared or not.

Example three:

I do not see much point for your third example. This is because you cannot use polymorphic types (if you pass an object obtained from Dependency, it will be sliced โ€‹โ€‹during the copy operation). Thus, the code can also be inside Addict, and not a separate class.

Bill Harlen: ( http://billharlan.com/pub/papers/Managing%5FCpp%5FObjects.html )

Do not take anything from Bill But:

  • I have never heard of him.
    • He is a geophysicist, not a programmer.
    • He recommends programming in Java to improve your C ++.
    • Languages โ€‹โ€‹are now so varied in use that it is completely false).
    • If you want to use What To-do / not-do links.
      Then I would select one of the big names in the C ++ field:
      Straustrup / Sutter / Alexandrescu / Meyers

Summary:

  • Do not use RAW pointers (if ownership is required)
  • Use smart pointers.
  • Do not copy objects to your object (it will cut).
  • You can use links (but you know the limitations).

My example of dependency injection using links:

 class Lexer { public: Lexer(std::istream& input,std::ostream& errors); ... STUFF private: std::istream& m_input; std::ostream& m_errors; }; class Parser { public: Parser(Lexer& lexer); ..... STUFF private: Lexer& m_lexer; }; int main() { CLexer lexer(std::cin,std::cout); // CLexer derived from Lexer CParser parser(lexer); // CParser derived from Parser parser.parse(); } // In test.cpp int main() { std::stringstream testData("XXXXXX"); std::stringstream output; XLexer lexer(testData,output); XParser parser(lexer); parser.parse(); } 
+8
Dec 29 '09 at 16:41
source share
โ€” -

Description: If you need to save the link, save the pointer as a private variable and access it through a method that dereferences it. You can check that the pointer is not null in the invariant of the object.

In depth:

First, storing references in classes makes it impossible to implement a reasonable and legal copy constructor or assignment operator, so they should be avoided. This is usually a mistake.

Secondly, the type of pointer / reference passed to functions and constructors should indicate who is responsible for freeing the object and how it should be freed:

  • std :: auto_ptr - the called function is responsible for the release and will do this automatically when it is done. If you need copy semantics, the interface should provide a clone method that should return auto_ptr.

  • std :: shared_ptr - the called function is responsible for the release and will do this automatically when it is done and when all other references to the object disappear. If you need surface copy semantics, the functions generated by the compiler will be fine, if you need deep copying, the interface should provide the clone method, which should return shared_ptr.

  • Help - the caller is responsible. You don't care - an object can be allocated to everything that you know. In this case, you must pass by reference, but save by pointer . If you need surface copy semantics, the functions generated by the compiler will be fine, if you need deep copying, you have problems.

  • Raw pointer. Who knows? Can be highlighted anywhere. May be null. You may be responsible for his release, or you may not.

  • Any other smart pointer - it should manage the lifetime for you, but you will need to look at the documentation to find out what the copying requirements are.

Note that the methods that give you the responsibility to free an object do not violate the DI - releasing an object is just part of the contract you have with the interface (since you do not need to know anything about a particular type in order to free it).

+4
Dec 29 '09 at 2:09
source share

[update 1]
If you can always guarantee that the addict survives the addiction, you can use a raw pointer / link, of course. between these two, I would make a very simple decision: pointer, if NULL is allowed, reference otherwise.

(The point of my initial post was that neither the pointer nor the link solves the problem of lifetime)




I would follow the shameful google style guide and use smart pointers.

Both the pointer and the link have the same problem: you need to make sure that the addict is going through the addict. This imposes a rather unpleasant responsibility on the client.

With the help of a smart pointer (link), the policy becomes dependent, is destroyed when no one else uses it. Sounds perfect to me.

Even better: with boost::shared_ptr (or a similar smart pointer that allows a neutral type destruction policy), the policy is tied to a building object, which usually means that everything that affects the dependency ends in one place.

Typical smart pointer issues โ€” overhead and circular links โ€” rarely come into play here. Usually instances of dependencies are not tiny and numerous, and a dependency that has a strong link to its addicts is at least the smell of code. (you still need to keep these things in mind. Welcome to C ++)

Warning: I am not a โ€œfully soldโ€ DI, but I am fully sold by smart signs;)

[update 2]
Note that you can always create shared_ptr on the stack / global instance using a zero debiter.
This requires that both parties support this, though: the addict must make sure that he does not give a link to addiction to someone else who can live longer, and the caller returned with responsibility that guarantees a lifetime. I am not happy with this solution, but sometimes used it.

+1
Dec 29 '09 at 13:56
source share

I would avoid references as members, since they usually do not cause headaches if you end up inserting one of your objects into an STL container. I would like to learn a combination of boost::shared_ptr for ownership and boost::weak_ptr for dependents.

+1
Dec 29 '09 at 13:56
source share

This has been asked before, but my SO search skills do not go as far as finding it. To summarize my position, you should very rarely, if ever, use links as members of a class. This leads to various initialization, assignment, and copying problems. Use a pointer or value instead.

Edit: Found one - this is a question with many opinions as answers: Should I prefer pointers or links in member data?

0
Dec 29 '09 at 13:44
source share

But then I get other, equally authoritative advice against using links as a member: http://billharlan.com/pub/papers/Managing%5FCpp%5FObjects.html

In this case, I think that you only want to set the object once in the constructor and never change it, so no problem. But if you want to change it later, use the init function, create a copy constructor, in short, everything that should change the link, you will have to use pointers.

0
Dec 29 '09 at 13:47
source share

I can go down here, but I will say that there should not be any reference members in the class FOR ANY REASO, EVER. Except when they are a simple constant value. There are many reasons for this, secondly, you start this, you open all the bad things in C ++. Check out my blog if you really care.

0
Dec 30 '09 at 10:33
source share



All Articles