5

I was told to avoid using pointers in C++. It seems that I can't avoid them however in the code i'm trying to write, or perhaps i'm missing out on other great C++ features.

I wish to create a class (class1) which contains another class (class2) as a data member. I then want class2 to know about class1 and be able to communicate with it.

I could have a reference to class1 as a member in class2 but that then means I need to provide a reference to class1 as a parameter in the constructor of class2 and use initialiser lists which I don't want. I'm trying to do this without needing the constructor to do it.

I would like for class2 to have a member function called Initialise which could take in the reference to class1, but this seems impossible without using pointers. What would people recommend here? Thanks in advance.

The code is completely simplified just to get the main idea across :

class class1
{
    public:
        InitialiseClass2()
        {
            c2.Initialise(this);
        }

    private:
        class2 c2;

};

class class2
{
    public:
        Initialise(class1* c1)
        {
            this->c1 = c1;
        }

    private:
        class1* c1;

};
Engineer999
  • 3,683
  • 6
  • 33
  • 71
  • Are you asking whether to use references instead of pointers, or whether you should use neither? – Jon Chesterfield Oct 15 '15 at 13:36
  • 5
    Its raw **owning** pointers which were to avoid. – Jarod42 Oct 15 '15 at 13:36
  • pointers are part of C++ and you can't avoid them all the time. Why do you want to avoid constructors and initializer list ? – Manos Nikolaidis Oct 15 '15 at 13:36
  • @Jarod42 Still, having pointer arithemtic when you don't need it isn't ideal. I'd probably opt for an `std::reference_wrapper`. Although it might not work with incomplete types... – juanchopanza Oct 15 '15 at 13:37
  • 2
    See [Why are pointers not recommended when coding with C++?](http://programmers.stackexchange.com/a/163279/2366) – Konrad Rudolph Oct 15 '15 at 13:39
  • Should I use references instead of pointers? – Engineer999 Oct 15 '15 at 13:41
  • In this case I don't want the construction of class2 to depend on passing a reference to class1. I'd like to use the default constructor and then later pass in the handler to class1 – Engineer999 Oct 15 '15 at 13:44
  • 1
    @Engineer999, should you use spoons insteads of forks? Same kind of question. Pointers are pointers, references are references. They have their own applicability. – SergeyA Oct 15 '15 at 13:44
  • @Engineer999: Seems like a design smell to me. What state is your object in between construction and a call to `Initialize()`? What happens if you perform operations on it in that state? – Lightness Races in Orbit Oct 15 '15 at 13:49
  • i'm starting to work on a big C++ project.. my first one I would say. I was told, do not use pointers in C++. There should be no need. This is the part I don't understand. In order to dynamically create objects on the free-store, don't we need pointers ? – Engineer999 Oct 15 '15 at 13:50
  • Of course you need pointers. Try putting references in the container. YOU DO NEED POINTERS in C++ programms. You just do now want to manage them. – SergeyA Oct 15 '15 at 13:52
  • @Engineer999: You're repeating vaguenesses, which isn't particularly helpful, but I shall assume you are asking "don't we need raw pointers, `new` and `delete`?" No. No, you do not. Look up `std::make_unique`. Look up `std::vector`. Which book are you using to learn C++? – Lightness Races in Orbit Oct 15 '15 at 13:54
  • None really. i was reading parts of Stroustrup's 4th edition and stuff online. I think my mind is stuck more in C and embedded systems )). I am fairly new to C++ and perhaps thinking too much about memory etc. when perhaps I should be thinking on a higher level – Engineer999 Oct 15 '15 at 14:18
  • You don't need pointers in C++. Never. That's why they were marked deprecated on the standard in 2001. :DDD – AndrewBloom Oct 15 '15 at 14:25
  • 1
    Pointers are used, in the Embedded Systems arena, to access hardware devices. In this case, raw pointers work a lot better (more efficient) than smart pointers. Also, there is no "memory" allocated and none to delete. – Thomas Matthews Oct 15 '15 at 15:38

5 Answers5

6

this seems impossible without using pointers

That is incorrect. Indeed, to handle a reference to some other object, take a reference into a constructor:

class class2
{
    public:
        class2(class1& c1)
           : c1(c1)
        {}

    private:
        class1& c1;
};

The key here is to initialise, not assign, the reference. Whether this is possible depends on whether you can get rid of your Initialise function and settle into RAII (please do!). After that, whether this is actually a good idea depends on your use case; nowadays, you can almost certainly make ownership and lifetime semantics much clearer by using one of the smart-pointer types instead — even if it's just a std::weak_ptr.

Anyway, speaking more generally.

Are pointers "always" bad? No, of course not. I'd almost be tempted to say that managing dynamic memory yourself is "always" bad, but I won't make a generalisation.

Should you avoid them? Yes.

The difference is that the latter is a guideline to steer you away from manual memory management, and the former is an attempted prohibition.

Lightness Races in Orbit
  • 378,754
  • 76
  • 643
  • 1,055
  • 1
    @Engineer999: With one of the many standard containers at your disposal. Your code will be more maintainable and bug-free if you use those where you can, instead of hacking around with the free store on your own. _That's the purpose of the advice you cite._ And if you want to exclude standard smart pointers from "pointers", there's always `std::make_{unique,shared}` which at least handles pointee lifetime for you. Are there occasions where you will have a good reason to use raw pointers? Yes! Use the right tool for the job. It's just that raw pointers are rarely the right tool for the job. – Lightness Races in Orbit Oct 15 '15 at 13:50
  • 1
    plus one from a carnivorous cat – Bathsheba Oct 15 '15 at 13:54
4

No, using pointers in C++ is not bad at all, and I see this anti-advice over and over again. What is bad is managing pointers by yourself, unless you are creating a pointer-managing low-level entity.

Again, I shall make a very clear distinction. Using pointers is good. Very few real C++ programs can do without USING pointers. Managing pointers is bad, unless you are working on pointer manager.

SergeyA
  • 61,605
  • 5
  • 78
  • 137
  • 1
    “I see this anti-advice over and over again” — really? I don’t. It’s almost exclusively about owning pointers, and then a bit about using references where appropriate. But once that’s taken into account the remaining uses of raw pointers are fairly few (though OP’s might be one). – Konrad Rudolph Oct 15 '15 at 13:40
  • @KonradRudolph, it is always a confirmation bias :) You have to trust me here, I personally have seen it everywhere, including coding guidelines of one of my former teams. – SergeyA Oct 15 '15 at 13:41
  • @KonradRudolph, even your own answer has the same issue: "modern C++ idioms often don’t need pointers at all" - from your link. Talk to me about non-usage of const char*. – SergeyA Oct 15 '15 at 13:43
  • My own answer is very much in line with my comment above: once you’ve eliminated legacy uses of raw pointers, there are not many remaining uses. Those that remain, however, are of course entirely valid (though, caveat, I’d prefer it if C++ had no privileged syntax for this type of pointers, but rather a class template `raw_ptr` or `address` to represent them). I disagree that my answer has an issue in that way. I’m not sure what you mean by “non-usage of `const char*`”. – Konrad Rudolph Oct 15 '15 at 13:45
  • @SergeyA: Perhaps you forgot to read the word "often"? `const char*` is like one of the very few examples of good pointer usage, and even then you could often deem it a microoptimisation in some sense. – Lightness Races in Orbit Oct 15 '15 at 13:45
  • @LightnessRacesinOrbit, here we are again. I can keep finding good usages of pointers, and you will keep saying it is not 'often'. Been there, done that. But saying that const char* is not often used... I am not sure what to reply with. – SergeyA Oct 15 '15 at 13:47
  • @LightnessRacesinOrbit, Konrad said 'often don't need'. I mentioned const char*. You said I misread 'often' - implying that const char* is not often? Or did you mean something else? – SergeyA Oct 15 '15 at 13:51
  • 2
    @SergeyA Incidentally, I think it’s clear that we all agree on the matter. We just disagree (fundamentally, I believe) on the phrasing. Here’s my perspective: this question once again shows how completely badly pointers in C++ are usually taught, resulting in bad, buggy, inefficient code. The way to break this cycle is to take the privileged position away from pointers in C++. Pointers should be thought of as just another type, to be used at discretion and with thought, not as the default go-to type. As a programmer, I use raw pointers where needed. As a teacher, I abhor C++ raw pointer syntax – Konrad Rudolph Oct 15 '15 at 13:51
  • @SergeyA: You've completely lost me now but I'm nonetheless fairly sure you misunderstood me and Konrad both. – Lightness Races in Orbit Oct 15 '15 at 13:52
  • How about containers of polymorphic objects? How would you do this without pointers? Not often enough? How about indexes to various heavy-weight object collections? Not often? – SergeyA Oct 15 '15 at 13:54
  • Um, `boost::ptr_{map,vector,etc}`? For an indexed collection wrap a `boost::bimap`.... These are solved problems and do not require pissing about with raw pointers in userspace. – Lightness Races in Orbit Oct 15 '15 at 13:55
  • Well sure, same thing if you think that using `std::vector` is "using pointers". Might as well just go home now – Lightness Races in Orbit Oct 15 '15 at 13:56
  • No, using vector is not using pointer. But using a vector of pointers is using a pointer. – SergeyA Oct 15 '15 at 13:57
  • @SergeyA Good question. Depends on usage. However, I don’t use OOP excessively; recently, I have often used functional ways of implementing polymorphism (tagged unions, i.e. `boost::variant` + visitors), or I encapsulate polymorphic objects into their own memory-managing classes (I think this is sometimes known as envelope–letter pattern?). – Konrad Rudolph Oct 15 '15 at 13:57
  • @SergeyA: Then it's a good thing nobody (but you) suggested using a vector of pointers. Using `boost::ptr_vector` is "using pointers" precisely as much as using `std::vector` is. – Lightness Races in Orbit Oct 15 '15 at 13:57
  • @KonradRudolph: I've been pretty `boost::variant`-heavy lately, too. – Lightness Races in Orbit Oct 15 '15 at 13:58
  • It's a pity you do not know what you are talking about, LRiO. _A ptr_vector is a pointer container that uses an underlying std::vector to store the pointers._ – SergeyA Oct 15 '15 at 13:58
  • Yes, and a `vector` is a data container that uses an underlying region of dynamically allocated memory, accessed through a `T*`, to store the data. The container uses pointers, but that usage is entirely abstracted away from the call site. That's the point. Kind of basic stuff, to be quite honest. – Lightness Races in Orbit Oct 15 '15 at 13:58
  • I am. To put elements into ptr_vector, you need to provide it with POINTERS. Do I need to remind you the prototype for `replace()`? It is accepting pointer, so when you call this function, you are using a pointer. What else? – SergeyA Oct 15 '15 at 14:01
  • @KonradRudolph, I am not a fan of OOP myself, this is certain. To me the much more relevant example is an index. – SergeyA Oct 15 '15 at 14:02
  • As it seems I can't budge you on your extremely broad definition of "using pointers", let's leave it there. – Lightness Races in Orbit Oct 15 '15 at 14:13
  • @KonradRudolph, oh, here you go: http://stackoverflow.com/questions/33157539/graph-structure-are-pointers-bad - another one saying that pointers are bad. – SergeyA Oct 15 '15 at 20:12
3

A pointer can be nullptr whereas a reference must always be bound to something (and cannot be subsequently re-bound to something else).

That's the chief distinction and the primary consideration for your design choice.

Memory management of pointers can be delegated to std::shared_ptr and std::unique_ptr as appropriate.

Bathsheba
  • 231,907
  • 34
  • 361
  • 483
0

well, I never had the need to 2 classes to have reciprocal reference and for good reasons, how do you know how to test those classes? If later you need to change something in the way the 2 classes communicates you will probably have to change code in both classes). You can workaround in many ways:

  1. You may need in reality just 1 class ( you have broken into much classes)
  2. You can register a Observer for a class (using a 3rd class, in that case you will end up with a pointer, but at least the 2 classes are less coupled and it is easier test them).
  3. You can think (maybe) to a new interface that require only 1 class to call methods on the other class
  4. You could pass a lambda (or a functor if you do not have C++11) into one of the methods of the class removing the need to a back reference
  5. You could pass a reference of the class inside a method.
  6. Maybe you have to few classes and in reality you need a third class than communicates with both classes.
  7. It is possible you need a Visitor (maybe you really need multiple dispatch)

Some of the workarounds above need pointers, some not. To you the choice ;)

NOTE: However what you are doing is perfectly fine to me (I see you do some trickery only in constructors, but probably you have more omitted code, in wich case that can cause troubles to you). In my case I "register" one class into another, then after the constructor called I have only one class calling the other and not viceversa.

CoffeDeveloper
  • 7,961
  • 3
  • 35
  • 69
0

First of all whenever you have a circular dependency in your design think about it twice and make sure it's the way to go. Try to use the Dependency inversion principle in order to analyze and fix your dependencies.

I was told to avoid using pointers in C++. It seems that I can't avoid them however in the code i'm trying to write, or perhaps i'm missing out on other great C++ features.

Pointers are a powerful programming tool. Like any other feature in the C++ (or in any programming language in general) they have to be used when they are the right tool. In C++ additionally you have access to references which are similar to pointers in usage but with a better syntax. Additionally they can't be null. Thus they always reference a valid object.

So use pointers when you ever need to but try to avoid using raw pointers and prefer a smart pointer as alternative whenever possible. This will protect you against some trivial memory leak problems but you still have to pay attention to your object life-cycle and for each dynamically allocated object you should know clearly who create it and when/whom will release the memory allocated for the object.

Pointers (and references) are very useful in general because they could be used to pass parameters to a method by reference so you avoid passing heavy objects by value in the stack. Imagine the case for example that you have a very big array of heavy objects (which copy/= operator is time consuming) and you would like to sort these objects. One simple method is to use pointers to these objects so instead of moving the whole object during the sorting operation you just move the pointers which are very lightweight data type (size of machine address basically).

rkachach
  • 16,517
  • 6
  • 42
  • 66