简体   繁体   中英

What's wrong with this class C++ declaration?

My Professor gave us this class and told us that it won't compile. He says that the donors array would conflict with the constructor. So... why would that be?

I think that the name of the Donor array might do it, but it shouldn't be the problem because the name of the member array donor is case sensitive, and is therefore different than the class name.

Here's the code:

#ifndef DONORS_H
#define DONORS_H

#include <string>

#include "name.h"
#include "donor.h"

using namespace std;

const int 
    DONORS_LOAD_ERROR = 1,
    DONORS_UPDATE_ERROR = 2,
    DONORS_ADD_ERROR = 3;

const int MAX_DONORS = 100;

class Donors {
public:
    Donors() : size(0) {}
    void load(string filename);
    int getSize() {return size;}
    int find(Name name);
    int add(Name name);
    int add(Name name, Donation donation, int ytd);
    void processDonation(Name name, Donation donation);
    void update(string filename);
    void print();
private:
    Donor donorsList[MAX_DONORS];
    int size;
};

#endif

The professor writes:

In this version, we've taken version 2, added constructors, and maximized the use of objects.

HOWEVER, the introduction of constructors breaks the declaration of the array data member inside the Donors class; therefore THIS VERSION DOES NOT COMPILE!!!!

I've been discussing this with a classmate and we're both stumped. What's up with this C++ class?

Edit:

The compiler messages are shown below:

编译器错误

It just occurred to me that the Donor class has a constructor. Being that we haven't touched vectors with a ten foot pole, how on earth are we supposed to compile this?

Edit2:

Here's the donor class:

#ifndef DONOR_H
#define DONOR_H

#include "name.h"
#include "donation.h"

using namespace std;

class Donor {
public:
    Donor(Name n, Donation ld=Donation(0, 0), int y=0) : name(n), lastDonation(ld), ytd(y) {}
    Name getName() {return name;}
    Donation getLastDonation() {return lastDonation;}
    int getYtd() {return ytd;}
    void processDonation(Donation d);
private:
    Name name;  
    Donation lastDonation;
    int ytd;
};

#endif

Difficult to say without the definition of the Donor class, but my guess is that he added a constructor with parameters to the Donor class, so it won't have the implicit default constructor any more.

But now, without a default constructor, that is a constructor that can be called without arguments, you cannot declare an array of such type, because there is no way to pass on the required parameters!

My guess is that the class Donor (which you didn't include) doesn't have a default constructor. If so, you will get a compiler error because this line:

Donor donorsList[MAX_DONORS]; 

Will try to use the constructor-with-no-parameters that Donor doesn't have.

It will help you to do better in the course if you experience the errors your instructor is showing you. Then when you experience them in real life you will recognize them. If you're going to learn C++, the strategy of "me and a friend looked at this and it should compile fine" is never going to be a substitute for trying to compile it and seeing what errors you get.

You need to include the definition of class Donor .

From the looks of it, I would guess that class Donor has a non-default constructor, ie the constructor that takes one or more parameters. In that case, the default constructor for class Donor is not generated by the compiler, and you have to define it yourself.

The default constructor is needed to create donorsList , because when you create an array of objects, the default constructor is called for each one.

Considering first Donors class:

#ifndef DONORS_H
#define DONORS_H

#include <string>

#include "name.h"
#include "donor.h"

OK so far.

using namespace std;

No! Never put using namespace std; in the global namespace in a header. The author (your professor) is almost guaranteeing silly name clashes like the compiler complaining about distance in some poor user's code.

const int 
    DONORS_LOAD_ERROR = 1,
    DONORS_UPDATE_ERROR = 2,
    DONORS_ADD_ERROR = 3;

const int MAX_DONORS = 100;

Those are bad names. Reserve shouting ALL UPPERCASE for macro names. But do use them for macro names.

Also, better use an enum for such constants.

And also, better use exceptions for failure reporting.

class Donors {
public:
    Donors() : size(0) {}
    void load(string filename);

The string argument would better be passed by reference to const .

    int getSize() {return size;}

This method should be const .

Also, while the prefix get has a practical advantage in some languages like Java and C#, which support instrospection (and hence tools based on introspection), in C++ it's just silly verbosity – in most cases, and in this case.

Better call that mathod just name .

Guideline: think about the readability of the calling code.

    int find(Name name);

The name argument would probably better be passed as reference to const .

    int add(Name name);

The name argument would probably better be passed as reference to const .

    int add(Name name, Donation donation, int ytd);

The name and donation arguments would probably better be passed as references to const .

The name ytd is very bad. It is very difficult to guess what it means.

    void processDonation(Name name, Donation donation);

The name and donation arguments would probably better be passed as references to const .

The interface is ambiguous: what is the difference between add and process ?

    void update(string filename);

The name argument would probably better be passed as reference to const .

The name of this method is bad.

It is nearly impossible to guess what this method does.

    void print();

Where exactly should this method print something, and in what format?

private:
    Donor donorsList[MAX_DONORS];

This declaration requires that Donor has a constructor that can be called without arguments, a default constructor .

    int size;
};

#endif

Considering the second Donor class:

#ifndef DONOR_H
#define DONOR_H

#include "name.h"
#include "donation.h"

OK so far.

using namespace std;

No! Never put using namespace std; in the global namespace in a header. The author (your professor) is almost guaranteeing silly name clashes like the compiler complaining about distance in some poor user's code.

class Donor {
public:
    Donor(Name n, Donation ld=Donation(0, 0), int y=0) : name(n), lastDonation(ld), ytd(y) {}

y and ytd are bad names. It is impossible to guess at this point what they're about.

ld is a bad name.

The Name and Donation arguments should probably better be passed by reference to const .

Note: when there is a user-declared constructor, such as the one above, then a default constructor is not generated automatically .

    Name getName() {return name;}

This method should be const .

Also, while the prefix get has a practical advantage in some languages like Java and C#, which support instrospection (and hence tools based on introspection), in C++ it's just silly verbosity – in most cases, and in this case.

Better call that mathod just name .

Guideline: think about the readability of the calling code.

    Donation getLastDonation() {return lastDonation;}

See comments above.

    int getYtd() {return ytd;}

See comments above.

    void processDonation(Donation d);

The argument should probably better be passed by reference to const .

private:
    Name name;  
    Donation lastDonation;
    int ytd;
};

#endif

In short, the Donors class requires that Donor has a default constructor, but since Donor has a user-declared constructor a default constructor is not generated.

One fix is to replace the simple array with a std::vector or other collection, eg

std::vector<Donor> donors_;

or

std::map< std::string, Donor > donors_;

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM