web-dev-qa-db-fra.com

Comment étendre correctement une classe en C ++ et écrire son fichier d'en-tête?

Remarque: J'ai modifié mon ancienne question pour qu'elle soit aussi claire que possible.

J'ai une bibliothèque tierce nommée person.lib Et son en-tête person.h. Ceci est ma structure de projet réelle et elle se compile et fonctionne parfaitement.

Structure réelle:

main.cpp

#include <iostream>
#include <time.h>
#include <ctype.h>
#include <string>
#include "person.h"
using namespace person;
using namespace std;

class Client : public Person 
{
    public:
        Client();   
        void onMessage(const char * const);
    private:
        void gen_random(char*, const int);
};

Client::Client() {
    char str[11];
    gen_random(str, 10);
    this->setName(str);
}

void Client::onMessage(const char * const message) throw(Exception &) 
{
    cout << message << endl;
}

void Client::gen_random(char *s, const int len) {
    //THIS FUNCTION GENERATES A RANDOM NAME WITH SPECIFIED LENGTH FOR THE CLIENT
}

int main()
{
    try
    {
        Person *p = new Client;
        p->sayHello();
    }
    catch(Exception &e)
    {
        cout << e.what() << endl;
        return 1;
    }
    return 0;
}

Maintenant, je veux refactoriser mon code en divisant la déclaration de ma classe Client de sa définition et créer client.h Et client.cpp. ATTENTION: sayHello() et onMessage(const * char const) sont des fonctions de la bibliothèque de personnes .

Structure refactorisée:

main.cpp

#include <iostream>
#include "client.h"
using namespace person;
using namespace std;

int main()
    {
        try
        {
            Person *p = new Client;
            p->sayHello();
        }
        catch(Exception &e)
        {
            cout << e.what() << endl;
            return 1;
        }
        return 0;
    }

client.cpp

#include "client.h"
using namespace person;
using namespace std;

Client::Client() {
    char str[11];
    gen_random(str, 10);
    this->setName(str);
}

void Client::onMessage(const char * const message) throw(Exception &) 
{
    cout << message << endl;
}

void Client::gen_random(char *s, const int len) {
    //THIS FUNCTION GENERATES A RANDOM NAME WITH SPECIFIED LENGTH FOR THE CLIENT
}

client.h

#ifndef CLIENT_H
#define CLIENT_H
#include <time.h>
#include <ctype.h>
#include <string>
#include "person.h"

class Client : public Person 
{
    public:
        Client();   
        void onMessage(const char * const);
    private:
        void gen_random(char*, const int);
};
#endif

Comme vous pouvez le voir, j'ai simplement créé un client.h Dans lequel il y a l'inclusion de la classe de base person.h, Puis j'ai créé client.cpp Dans lequel il y a l'inclusion de client.h Et les définitions de ses fonctions. Maintenant, la compilation me donne ces erreurs:

error C2504: 'Person': base class undefined     client.h    7   1   Test
error C2440: 'inizialization': unable to convert from 'Client *' to 'person::impl::Person *'   main.cpp 15  1   Test
error C2504: 'Person': base class undefined     client.h    7   1   Test
error C2039: 'setName': is not a member of 'Client'  client.cpp 8   1   Test
error C3861: 'sendMessage': identifier not found    client.cpp  34  1   Test

C'est un refactoring simplement coupé et copié mais cela ne fonctionne pas et je ne comprends vraiment pas POURQUOI! Quelle est la solution et pourquoi cela me donne ces erreurs? Y a-t-il quelque chose sur la structure C++ qui me manque? Je vous remercie.

10
Angelo

Voici une implémentation dog-n-bird (ruff ruff, cheep cheep) cLawyer est défini et implémenté dans main.cpp, tandis que cPerson et cClient sont définis dans leurs propres fichiers d'en-tête, implémentés dans leur propre fichier cpp. Une meilleure approche consisterait à stocker le nom de la classe. Ensuite, on n'aurait pas besoin de surcharger la méthode speak - on pourrait simplement définir le className dans chaque copie dérivée. Mais cela aurait fourni dans mes estimations, un exemple moins utile pour vous.

main.cpp

#include <cstdio>
#include "cClient.h"

class cLawyer : public cPerson
{
    public:
        cLawyer() : cPerson() {}
        ~cLawyer() {}
        void talk(char *sayWhat){printf("cLawyer says: '%s'\n", sayWhat);}
};

int main()
{
    cPerson newPerson;
    cClient newClient;
    cLawyer newLawyer;

    newPerson.talk("Hello world!");
    newClient.talk("Hello world!");
    newLawyer.talk("Hello $$$");

    return 0;
}

cPerson.h

#ifndef cPerson_h_
#define cPerson_h_

class cPerson
{
    public:
        cPerson();
        virtual ~cPerson();
        virtual void talk(char *sayWhat);
    protected:
    private:
};

#endif // cPerson_h_

cPerson.cpp

#include "cPerson.h"
#include <cstdio>
cPerson::cPerson()
{
    //ctor
}

cPerson::~cPerson()
{
    //dtor
}

void cPerson::talk(char *sayWhat)
{
    printf("cPerson says: '%s'\n",sayWhat);
}

cClient.h

#ifndef cClient_h_
#define cClient_h_

#include "cPerson.h"

class cClient : public cPerson
{
    public:
        cClient();
        virtual ~cClient();
        void talk(char *sayWhat);
    protected:
    private:
};

#endif // cClient_h_

cClient.cpp

#include "cClient.h"
#include <cstdio>

cClient::cClient()
{
    //ctor
}

cClient::~cClient()
{
    //dtor
}

Sortie

cPerson says: 'Hello world!'
cClient says: 'Hello world!'
cLawyer says: 'Hello $$$'

Suggestions notées ci-dessus:

//In the cPerson class, a var
  char *m_className;

//In the cPerson::cPerson constructer, set the var
  m_className = "cPerson";

//Re-jig the cPerson::speak method
void cPerson::speak(char *sayWhat)
{
  printf("%s says: '%s'\n", m_className, sayWhat);
}

// EDIT: *** remove the speak methods from the cClient and cLawyer classes ***

//Initialize the clas name apporpriately in derived classes
//cClient::cClient
  m_className = "cClient";


//Initialize the clas name apporpriately in derived classes
//cLaywer::cLaywer
  m_className = "cLawyer";
7
enhzflep

Vous déclarez la classe Client deux fois - une fois dans le .h fichier et une fois dans .cpp. Il vous suffit de le déclarer dans le .h fichier.
Vous devez également mettre le using namespace person; à la .h fichier.
Si la classe Personne est dans la personne d'espace de noms, utilisez le person::Person pour y accéder.

Le client.cpp doit contenir uniquement des définitions!

Je pense que pour l'éditeur de liens, la classe Client définie dans client.h et la classe Client définie dans client.cpp sont des classes différentes, donc il ne peut pas trouver l'implémentation de Client :: Client (). Je souhaite supprimer la déclaration de classe Client du client.cpp et y laisser uniquement des définitions de fonctions:

// client.cpp

#include <time.h>
#include <ctype.h>
#include <string>
#include "client.h"
using namespace std;

Client::Client()
{
    //DO STUFF
}

void Client::onMessage(const char * const message)
{
    //DO STUFF
}


void Client::gen_random(char *s, const int len) {
    //DO STUFF
}
5
Shimon Rachlenko