0

I am implementing a LinkedList in my project and this library has an example called ClassList on how to use the library. Now, I am programming for couple of years now, but I am fairly new to C++, so I would be grateful if I would just get some simple explanations on couple of questions that I have on why the example was written the way it was. The original code of the example is like this:

#include <LinkedList.h>

// Let's define a new class
class Animal {
    public:
        char *name;
        bool isMammal;
};

char  catname[]="kitty";
char  dogname[]="doggie";
char  emuname[]="emu";

LinkedList<Animal*> myAnimalList = LinkedList<Animal*>();

void setup()
{

    Serial.begin(9600);
    Serial.println("Hello!" );

    // Create a Cat
    Animal *cat = new Animal();
    cat->name = catname;
    cat->isMammal = true;

    // Create a dog
    Animal *dog = new Animal();
    dog->name = dogname;
    dog->isMammal = true;

    // Create a emu
    Animal *emu = new Animal();
    emu->name = emuname;
    emu->isMammal = false; // just an example; no offense to pig lovers

    // Add animals to list
    myAnimalList.add(cat);
    myAnimalList.add(emu);
    myAnimalList.add(dog);
}

void loop() {

    Serial.print("There are ");
    Serial.print(myAnimalList.size());
    Serial.print(" animals in the list. The mammals are: ");

    int current = 0;
    Animal *animal;
    for(int i = 0; i < myAnimalList.size(); i++){

        // Get animal from list
        animal = myAnimalList.get(i);

        // If its a mammal, then print it's name
        if(animal->isMammal){

            // Avoid printing spacer on the first element
            if(current++)
                Serial.print(", ");

            // Print animal name
            Serial.print(animal->name);
        }
    }
    Serial.println(".");

    while (true); // nothing else to do, loop forever
}

And then I tried to modify the code without using (so much) pointers:

#include <LinkedList.h>

// Let's define a new class
class Animal {
    public:
        char *name;
        bool isMammal;
};

char  catname[]="kitty";
char  dogname[]="doggie";
char  emuname[]="emu";

LinkedList<Animal> myAnimalList = LinkedList<Animal>();

void setup()
{

    Serial.begin(9600);
    Serial.println("Hello!" );

    // Create a Cat
    Animal cat;
    cat.name = catname;
    cat.isMammal = true;

    // Create a dog
    Animal dog;
    dog.name = dogname;
    dog.isMammal = true;

    // Create a emu
    Animal emu;
    emu.name = emuname;
    emu.isMammal = false; // just an example; no offense to pig lovers

    // Add animals to list
    myAnimalList.add(cat);
    myAnimalList.add(emu);
    myAnimalList.add(dog);
}

void loop() {

    Serial.print("There are ");
    Serial.print(myAnimalList.size());
    Serial.print(" animals in the list. The mammals are: ");

    int current = 0;
    Animal *animal;
    for(int i = 0; i < myAnimalList.size(); i++){

        // Get animal from list
        animal = &myAnimalList.get(i);

        // If its a mammal, then print it's name
        if(animal->isMammal){

            // Avoid printing spacer on the first element
            if(current++)
                Serial.print(", ");

            // Print animal name
            Serial.print(animal->name);
        }
    }
    Serial.println(".");

    while (true); // nothing else to do, loop forever
}

Both versions compile and work just fine. So my question is, why would there be used pointers to the objects in the original example, instead of the objects themselves as in the second example? Also, what is the "right" way to approach this matter? I do understand the concept of pointers, the question is more about why as about how.

Then the second question that I have is, why is there in the example provided also a pointer as a member variable "*name" used, and not the "actual" member variable?

And the last is, I went through some videos about classes in C++ and how to instantiate the objects, and I noticed that there are couple of ways, how people are doing it, and all of them work, as for example:

LinkedList<Animal> myAnimalList = LinkedList<Animal>();
// or
LinkedList<Animal> myAnimalList();
// or
LinkedList<Animal> myAnimalList;

Animal cat = new Animal();
// or
Animal cat();
// or
Animal cat;

I understand that it might differ with the parenthasis, if the constructor actually needs parameters to be fed to it, but if we put that aside, is there any difference between these three examples?

Thanks in advance for your time.

Yksisarvinen
  • 18,008
  • 2
  • 24
  • 52
f.bele
  • 207
  • 2
  • 13
  • 2
    Videos and online tutorials are not a good source for learning c++. I would suggest a good [book](https://stackoverflow.com/questions/388242). – t.niese May 18 '20 at 19:06
  • 1
    Your `myAnimalList.add(cat)` without pointers will store a copy of the `Animal` instance in that linked list, that might be what you want but not in every situation. – t.niese May 18 '20 at 19:13
  • 2
    `Animal` class is a common example for polymorphism. Polymorphism in C++ requires pointers or references. Perhaps that was the intent? – Yksisarvinen May 18 '20 at 19:14
  • 2
    @Yksisarvinen Looking at the linked example code, and some of the writing/terminology in the project's README, I think it was just an accident or misunderstanding. – Asteroids With Wings May 18 '20 at 19:15

1 Answers1

3

Both versions compile and work just fine. So my question is, why would there be used pointers to the objects in the original example, instead of the objects themselves as in the second example? Also, what is the "right" way to approach this matter? I do understand the concept of pointers, the question is more about why as about how.

There was no reason to use pointers. Your version is good.

Why were there so many pointers? Because there are bad programmers out there. ;)

The original code in fact has three memory leaks as a result.


Then the second question that I have is, why is there in the example provided also a pointer as a member variable "*name" used, and not the "actual" member variable?

The char* is a pointer-to-chars, a C-style string. If you had a char you'd only be storing one character.

In reality, the class should encapsulate a proper string type that owns the data it represents.


is there any difference between these three examples?

Yes, very much: two of them won't compile.

Animal cat = new Animal();

This dynamically-allocates an Animal, then attempts to copy-initialise an Animal from the pointer that new gives you. That's not valid. The "real" code would have been Animal* cat = new Animal(), but it's not necessary to use dynamic allocation here.

Animal cat();

This declares a function called cat returning an Animal. Not what you meant.

Finally:

LinkedList<Animal> myAnimalList = LinkedList<Animal>();

This is equivalent to LinkedList<Animal> myAnimalList; (well, it needs copyability pre-C++17, but never mind). It's pointless to write it out like that.

Asteroids With Wings
  • 17,071
  • 2
  • 21
  • 35
  • 1
    The author also calls an emu a "pig"; I generally question his or her knowledge on the topics surrounding the creation of the linked example file... – Asteroids With Wings May 18 '20 at 19:12
  • I didn't even want to go there with emus :) That with the memory leaks is something that got my attention. Would you kindly elaborate more to me? Just for the future reference what to pay attention to. Which principle on instantiating the objects in your opinion is generally better way to go? Thanks. – f.bele May 18 '20 at 19:36
  • 1
    There's no general rule, but you seem to have it right so far. Keep it as simple as you can. And if you _do_ need to use `new` (rare!) you'll also need `delete` somewhere. – Asteroids With Wings May 18 '20 at 19:37