-2

In the image what is written in black are variables specific for each class and what is blue is inherited from the client class. This is pretty much what I want to doI have class project in C++ about an SMMA where I have these classes: Client, Campaign, Analytics, Invoice, Post. Note that I must use inheritance.

#include<iostream>
#include<cstring>
#include<vector>
using namespace std;

class IOinterface{
public:
    virtual istream& input(istream&) = 0;
    virtual ostream& output(ostream&) const = 0;
};

class Client {
    protected:
        char* client_name;
        const int clientID;
        static int numberOfClients;
        string company;
        long marketing_budget;
        bool contract;
    public:
        void setClientName(const char* newClientName) {
            if(this->client_name != nullptr) {
                delete[] this->client_name;
                this->client_name = nullptr;
            }
            this->client_name = new char [strlen(newClientName) + 1];
            strcpy(this->client_name, newClientName);
        }
        const char* getClientName() const {return this-> client_name;}

        void setCompany(string newCompany) {this->company = newCompany;}
        string getCompany() {return this->company;}

        
        void setMarketingBudget(long newMarketingBudget) {this->marketing_budget = newMarketingBudget;}
        long getMarketingBudget() {return this->marketing_budget;}
    

        void setContract(bool newContract) {this->contract = newContract;}
        bool getContract() {return this->contract;}

        Client();
        Client(const char* client_name, const string company, const long marketing_budget, const bool contract);
        Client(const Client& obj);

        Client& operator=(const Client& obj);

        ~Client();

        friend istream& operator>>(istream& in, Client& obj);
        friend ostream& operator<<(ostream& out, const Client& obj);

};

int Client::numberOfClients = 0;

Client::Client():clientID(++numberOfClients){
    this->client_name = new char[strlen("NULL") + 1];
    strcpy(this->client_name, "NULL");
    this->company = "NULL";
    this->marketing_budget = 0;
    this->contract = 0;
}

Client::Client(const char* client_name, const string company, const long marketing_budget, const bool contract):clientID(++numberOfClients) {
    this->client_name = new char[strlen(client_name) + 1];
    strcpy(this->client_name, client_name);
    this->company = company;
    this->marketing_budget = marketing_budget;
    this->contract = contract;
}

Client::Client(const Client& obj):clientID(++numberOfClients) {
    this->client_name = new char[strlen(obj.client_name) + 1];
    strcpy(this->client_name, obj.client_name);
    this->company = obj.company;
    this->marketing_budget = obj.marketing_budget;
    this->contract = obj.contract;
}

Client& Client::operator=(const Client& obj) {
    if(this != &obj) {
        if(this->client_name != nullptr) {
            delete[] this->client_name;
            this->client_name = nullptr;
        }
        this->client_name = new char[strlen(obj.client_name) + 1];
        strcpy(this->client_name, obj.client_name);
        this->company = obj.company;
        this->marketing_budget = obj.marketing_budget;
        this->contract = obj.contract;
    }
    return *this;
}

Client::~Client() {
    if (this->client_name != nullptr) {
        delete[] this->client_name;
        this->client_name = nullptr;
    }
}

istream& operator>>(istream& in, Client& obj) {
    cout << "Client name: ";
    char name[150];
    in.getline(name, 150);
    if (obj.client_name != nullptr) {
        delete[] obj.client_name;
        obj.client_name = nullptr;
    }
    obj.client_name = new char[strlen(name) + 1];
    strcpy(obj.client_name, name);
    cout << "Company: ";
    getline(in, obj.company);
    cout << "Marketing budget: ";
    in >> obj.marketing_budget;
    cout << "Contract: ";
    in >> obj.contract;

    return in;
}

ostream& operator<<(ostream& out, const Client& obj) {
    out << "---------------------------------------------------" << endl;
    out << "ClientID: " << obj.clientID << "\n";
    out << "Client name: "<< obj.client_name << "\n";
    out << "Number of clients: " << obj.numberOfClients << "\n";
    out << "Company: " << obj.company << "\n";
    out << "Marketing budget: " << obj.marketing_budget << "\n";
    out << "Contract: " << obj.contract << "\n";
    out << "---------------------------------------------------" << endl;

    return out;
};

class Campaign : public IOinterface, virtual public Client {
    private:
        char* campaign_name;
        vector<string> objectives;
        string socialMediaPlatform;
    public:
        void setCampaignName(const char* newCampaignName) {
            if(this->campaign_name != nullptr) {
                delete[] this->campaign_name;
                this->campaign_name = nullptr;
            }
            this->campaign_name = new char[strlen(newCampaignName) + 1];
            strcpy(this->campaign_name, newCampaignName);
        }
        const char* getCampaignName() {return this->campaign_name;}

        void setObjectives(const vector<string>& newObjectives) {this->objectives = objectives;}
        const vector<string>& getObjectives() {return this-> objectives;}

        void setSocialMediaPlatform(string newSocialMediaPlatform) {this->socialMediaPlatform = newSocialMediaPlatform;}
        string getSocialMediaPlatform() {return this->socialMediaPlatform;}

        Campaign();
        Campaign(const char* campaign_name, const vector<string> objectives, const string socialMediaPlatform);
        Campaign(const Campaign& obj);

        Campaign& operator=(const Campaign& obj);

        ~Campaign();

        friend istream& operator>>(istream& in, Campaign& obj);
        friend ostream& operator<<(ostream& out, const Campaign& obj);

};

Campaign::Campaign() {
    this->campaign_name = new char[strlen("NULL") + 1];
    strcpy(this->campaign_name, "NULL");
    this->objectives = {};
    this->socialMediaPlatform = "NULL";
};

Campaign::Campaign(const char* campaign_name, const vector<string> objectives, const string socialMediaPlatform) {
    this->campaign_name = new char[strlen(campaign_name) + 1];
    strcpy(this->campaign_name, campaign_name);
    this->objectives = objectives;
    this->socialMediaPlatform = socialMediaPlatform;
}

Campaign::Campaign(const Campaign& obj) {
    this->campaign_name = new char[strlen(obj.campaign_name) + 1];
    strcpy(this->campaign_name, obj.campaign_name);
    this->objectives = obj.objectives;
    this->socialMediaPlatform = obj.socialMediaPlatform;
}

Campaign& Campaign::operator=(const Campaign& obj) {
    if (this != &obj) {
        if(this->campaign_name != nullptr) {
            delete[] this-> campaign_name;
            this->campaign_name = nullptr;
        }
        this->campaign_name = new char[strlen(obj.campaign_name) + 1];
        strcpy(this->campaign_name, obj.campaign_name);
        this->objectives = obj.objectives;
        this->socialMediaPlatform = obj.socialMediaPlatform;
    }
    return *this;
}

Campaign::~Campaign() {
    if(this->campaign_name != nullptr) {
        delete[] this->campaign_name;
        this->campaign_name = nullptr;
    }
}

istream& operator>>(istream& in, Campaign &obj) {
    cout << "Campaign name: ";
    char name[100];
    in.getline(name, 100);
    if(obj.campaign_name != nullptr) {
        delete[] obj.campaign_name;
        obj.campaign_name = nullptr;
    }
    obj.campaign_name = new char[strlen(name) + 1];
    strcpy(obj.campaign_name, name);
    cout << "Objectives: ";
    string temp;
    while(getline(in, temp)) {
        if (temp.empty()) { 
            break;
        }
        obj.objectives.push_back(temp);
        cout << "Enter another objective (or press Enter to finish): ";
    }
    cout << "Social media platform: ";
    in >> obj.socialMediaPlatform;

    return in;
}

ostream& operator<<(ostream& out, const Campaign& obj) {
    out << "---------------------------------------------------" << endl;
    out << "Campaign name: " << obj.campaign_name << "\n";
    out << "Objectives: ";
    for (int i = 0; i < obj.objectives.size(); i++)
        if (i == 0) {
            out << "-" << obj.objectives[i] << endl;
        } else {
            out << "            -" << obj.objectives[i] << endl;
        }
    out << "Social media platform: " << obj.socialMediaPlatform << "\n";
    out << "---------------------------------------------------" << endl;

    return out;
}



int main() {
    Campaign a("pix", {"bani", "likes"}, "Instagram"), b;
    cin>>b;
    cout <<b;

}

I want when I type

int main() {
 Client A;
 Campaign B;
 cin >> A >> B;
 cout << B;
}

to be prompted to enter details about a Client object, then details about a Campaign object, then when it executes "cout << B" I want to see on the screen all the information specific to the Campaign object and also some things inherited from the Client object. Instead, for those things inherited from the Client object, it shows NULL or 0, like in the default constructor of the class.

  • First thing : don't use `using namespace std` certainly not in a header file (where you declare your interface). Don't use `char* client_name` but use std::string (why include and not ?). Also use `nullptr` not `NULL` – Pepijn Kramer May 16 '23 at 11:15
  • Your main function has two `Client` objects. It has one in the variable `A` and it has another in the variable `B` because `Campaign` inherits from `Client`. When your print out `B` you are going to see the second of these objects not the first. That's just how it is. There's no connection between `A` and `B` (in this code) and any information that you enter into `A` is not going to show up in `B`. – john May 16 '23 at 11:17
  • 2
    It doesnt compile https://godbolt.org/z/f5ch41Whv. Please post a [mcve] and be precise about example input, output and expected output – 463035818_is_not_an_ai May 16 '23 at 11:17
  • To use "<<" you need to overload freestanding functions `std::ostream& operator<<(std::ostream& os, const Client& client)` and similarly for operator>>. You can't do this via a constructor. – Pepijn Kramer May 16 '23 at 11:18
  • `istream& operator>>(istream& in, Client& obj)` should not prompt for user input. The advantage of providing a `>>` for `istream` is that you can read a `Client` from a file, from a stringstream, from any stream you like. Yours only makes sense when input is taken from the user via console input. – 463035818_is_not_an_ai May 16 '23 at 11:18
  • Your code is half "C" half "C++" so maybe learn a bit more C++ first from : [a recent C++ book](https://stackoverflow.com/questions/388242/the-definitive-c-book-guide-and-list) or from [learncpp.com](https://www.learncpp.com/) that site is decent and pretty up to date. Then use [cppreference](https://en.cppreference.com/w/) for reference material (and examples). When you have done all that keep using [C++ core guidelines](https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines) to keep up to date with the latest best practices since C++ keeps evolving. – Pepijn Kramer May 16 '23 at 11:19
  • 1
    I would also add that this is a clear misuse of inheritance. `Client` and `Campaign` are not similar concepts and there is no reason for `Campaign` to inherit from `Client`. It might be reasonable for a `Campaign` to have a `Client` as a member variable, but inheritance is the wrong way to express any relationship. – john May 16 '23 at 11:19
  • 2
    Since the code is aware of `std::string`, why is it also using manually managed `char` arrays for strings? – Eljay May 16 '23 at 11:33

2 Answers2

0

Here's how you can achieve what you want with your existing code

int main()
{
    Client A;
    cin >> A;
    Campaign B;
    cin >> B;
    B.setClientName(A.getClientName());
    B.setCompany(A.getCompany());
    // etc etc
}

Essentially you have to copy all the client information from A to B.

This is not a good design. Inheritance should be used to model relationships when one class is a specialization of another class, e.g. a Circle is a Shape. These kind of relationships are sometimes called is-a relationships. When one class has another class as an attribute those are called has-a relationships, e.g. a Campaign has a Client. These kind of relationships are best modelled using member variables not inheritance. So your code would be much better rewritten to use membership instead of inheritance something like this

class Client
{
    ...
};

class Campaign // no inheritance from Client
{
public:
    Client getCLient() const { return client; }
    void setClient(const Client& c) { client = c; }
    ...
private:
    ...
    Client client; // client member
};

int main()
{
    Client A;
    cin >> A;
    Campaign B;
    cin >> B;
    B.setClient(A);
}
john
  • 85,011
  • 4
  • 57
  • 81
0

One traditional reason inheritance was invented was to re-use code in base classes. Client has an input operator; the input operator for the derived class Campaign should first call the input operator for the Client base class, re-using that existing code, and then prompt for the data members added in the derived class. This mimics the way constructors work, "inside-out"; but as opposed to compiler-generated constructors, you have to explicitly code that with an input function.

Whether that design is good is a different question; in the past two or three decades this kind of relative strong coupling has lost its appeal (and, for example, inheritance is often replaced with injection/membership).

Peter - Reinstate Monica
  • 15,048
  • 4
  • 37
  • 62