2

I'm a newbie at C++ and I was programming this code below.

This is DOCUMENT.h: It works well and therefore I didn't post its implementation (document.cpp):

#pragma once
#include "stdafx.h"
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
class Document {
private: 
    int id;
    char *titre;

public:
    Document();
    Document(int id, char *titre);
    ~Document();
    Document(const Document &doc);
    Document operator+( Document doc);
    Document operator=(const Document &doc);
    void setId(int id);
    int getId();

    void setTitre(char *titre);
    char *getTitre();
};

class Livre :public Document {
private:
    float price;

public:
    Livre();
    Livre(int id , char *titre,float price);
    ~Livre();
    Livre( Livre &doc);
    Livre operator+(Livre doc);
    Livre operator=( Livre &doc);

    void setPrice(float price);
    virtual float getPrice();
};

class Article :public Document {
private:
    char *date;

public:
    Article();
    Article(int id, char *titre , char *date);
    ~Article();
    Article(Article &doc);
    Article  operator+( Article &doc);
    Article operator=( Article &doc);
    void setDate(char* date);
    char *getDate();
};

MANAGER.H:

#include "Document.h"

class Manager {
private:
    Document **doc ;

public:
    int count = 0;

    Manager();
    ~Manager();

    int  ajouter(Document *d);
    void afficher();

};

MANAGER.CPP:

#include "stdafx.h"
#include "Manager.h"

Manager::Manager() {}
Manager::~Manager() {}

int  Manager::ajouter(Document *d) {
    if (count == 0) {
        doc = (Document**)malloc( sizeof(Document));
        doc[count] = new Document();

        doc[0] = d;
    }
    else {
        doc = (Document**)realloc(doc, count * sizeof(Document));
        doc[count] = new Document();
        doc[count] = d;
    }
    count++;

    return 0;
}

void Manager::afficher() {
    for (int i = 0; i < count; i++) {
        printf("\nid : %d  \n", doc[i]->getId());
        printf("titre : %s  \n", doc[i]->getTitre());
        printf("Price : %f  \n", doc[i]->getPrice());
    }

}

My problem is in afficher() function. I want to call getprice() from there but, getprice() is not a member of class Document : it's member of Livre class (which is a child of Document). Is there any way to call getprice() ?

Christophe
  • 68,716
  • 7
  • 72
  • 138
hamza mon
  • 97
  • 1
  • 9
  • `malloc` and `realloc` are being used incorrectly. Document should have a `virtual float getPrice();` (which probably ought to be `const`) if you want to use it in the way you describe. – Eljay Jan 12 '20 at 13:45
  • Your program has undefined behavior because of your use of `malloc` and `realloc`. The valid uses for both are so rare in C++ that I would recommend you forget that they exist. The likelihood that you are going to use them correctly as a beginner is very small. Always use `new[]` instead or rather, don't use dynamic allocation at all and use `std::vector` and `std::string` instead (and `std::unique_ptr` for single object allocations). You should almost never have to use `new`/`delete` either. – walnut Jan 12 '20 at 13:48
  • Does [this](https://godbolt.org/z/MgfRRy) answer you question? I can make it into full answer if you want with more explanation. – Hawky Jan 12 '20 at 13:53
  • If you want to call a member function of `Livre`, you need to create objects of type `Livre`, not its base class `Document`. Do you expect `Manager` to hold pointers to both `Document` and `Livre` instances or only the latter? If the former, what you want to happen if `doc[i]` *doens't* point to an object of type `Livre`? – walnut Jan 12 '20 at 13:59

1 Answers1

0

Solution of your problem

Ideally, Manager::afficher() should not have to know the details of Document. If should delagate this to a new function Document::afficher() and let each type of document decide on what information is to be printed and what is available. That's the principle of polymorphism:

void Manager::afficher() {
    for (int i = 0; i < count; i++) {
        doc[i]->afficher();
    }
}

If you want nevertheless to infringe the law of demeter, you can keep your approach. But you can only use the elements that are available for all the documents. Unless you know for sure it's a Livre. In this case you could use dynamic casting:

void Manager::afficher() {
    for (int i = 0; i < count; i++) {
        printf("\nid : %d  \n", doc[i]->getId());
        printf("titre : %s  \n", doc[i]->getTitre());
        Livre *l = dynamic_cast<Livre*>(doc[i]);  // attempt to see it's a Livre
        if (l) {                     // If it's a Livre l is not null 
            printf("Price : %f  \n", doc[i]->getPrice());
        }
        else printf ("Price : unknown\n"); 
    }
}

Not related, but your code has a lot of issues you are not yet aware of

First of all C++ is not C so malloc() should not be used unless you're sure of what you do (i.e. if you know the rules when to use a placement-new or not ).

Furthermore your management of pointers of pointers in Manager will lead you directly to a catastrophe: you allocate a single Document, You could eventually handle this as an array of a single document, but with casting wizardry, you make it a pointer to pointer. Ouch !

You need to change the way you manage your collection of documents:

   doc = (Document**)malloc( sizeof(Document));   //!!!!! NOOO

Since you want to use polymorphism having different kind of specialized documents, you could make your class look like:

class Manager {
private:
    vector<shared_ptr<Document>> doc ;  // safer way

public:
    Manager();
    ~Manager();
    int  ajouter(shared_ptr<Document> d);
    void afficher();
};

shared_ptr<> are used like pointers, but manage the memory by themselves. vector is auto-adatptative: you just need to push_back() an element and it will resize itself if needed. After, you can index the elements as if with array. In addition in counts for you with vector::size();

int  Manager::ajouter(shared_ptr<Document> d) {
    doc.push_back(d); 
}
int Manager::compter() {       // you need to add this in your class definition
    return doc.size();         // always up-to date count ! 
}

How to add new documents to your library ?

auto d1 = make_shared<Document>(); 
... 
manager.ajouter(d1); 
auto d2 = male_shared<Livre>(); 
...
manager.ajouter(d2); 

For this to work smoothly, you should also replace your char* with string to avoid memory management issues and get the power of C++ strings.

The dynamic casting that I mentioned in my solution works slightly differntly with shared pointers: you have to use dynamic_pointer_cast instead of dynamic_cast.

A last remark. Whenever you have a single virtual function in a class, you should make the destructor virtual as well.

Christophe
  • 68,716
  • 7
  • 72
  • 138