Reputation: 2350
I have the code:
#include <iostream>
#include <cstdlib>
using namespace std;
class Animal{
private:
int age;
public:
Animal() : age(1) {}
void toString(){
cout << "Age: " << age << endl;
}
};
class Cat : public Animal
{
public:
Cat() : age(5) {}
/*
void toString(){
cout << "Age: " << age << endl;
}*/
private:
int age;
};
int main(){
Cat tom;
tom.toString();
system("pause");
return 0;
}
But when I run the program, the age of the tom variable is 1, not 5. Does the toString can not read the age variable? If we open the /* */ the toString method in the Cat class, the age will be 5 !
(My english is not good very much. Thanks)
Upvotes: 3
Views: 2666
Reputation: 264381
Try:
#include <iostream>
#include <cstdlib>
using namespace std;
class Animal{
private:
int age;
public:
Animal(int a = 1) // Pass in the age as a parameter.
: age(a) // Default to 1.
{}
// Prefer generic print function rather than toString()
friend std::ostream& operator<<(std::ostream& s, Animal const& a) {
return s << "Age: " << a.age << '\n'; // Prefer '\n' rather than endl
// Unless you really want to flush
// the stream (this is not usually
// the case).
}
};
class Cat : public Animal
{
public:
Cat()
: Animal(5) // Now you can call the base class constructor
{} // And get it to set 5
private:
// int age; // don't have a private copy here.
// use the one that is available in the base class.
// Prefer generic print function rather than toString()
friend std::ostream& operator<<(std::ostream& s, Cat const& a)
{
// Print Cat
// Then use the Animal priting function to print more information about the object.
return s << "A Cat: " << static_cast<Animal const&>(*a);
}
};
int main(){
Cat tom;
// tom.toString(); // Don't use a toString() method.
// overload operator<< to print to a stream.
// If you want to convert to a string the just print
// to a string stream.
std::cout << tom;
system("pause");
return 0;
}
Upvotes: 0
Reputation: 17708
The problem is that Cat
is writing to the age
variable in Cat
, while toString()
reads the age
variable in Animal
, which, with Animal
's constructor, is initialized to 1
.
To solve this, you can provide another constructor for Animal
which accepts an age
parameter which is used to initialize Animal
's age
member variable.
class Animal{
private:
int age;
public:
Animal() : age(1) {}
Animal(int param_age) : age(param_age) {} // Initialize member variable age with parameter
void toString(){
cout << "Age: " << age << endl;
}
};
class Cat : public Animal
{
public:
Cat() : Animal(5) {} // Call Animal's constructor that set's the age
};
UPDATE: Another solution is to add a setter method in Animal
class that sets its age. You can then call it in Cat
's constructor to set the proper age.
class Animal{
private:
int age;
public:
Animal() : age(1) {}
void setAge(int age) { this->age = age; }
void toString(){
cout << "Age: " << age << endl;
}
};
class Cat : public Animal
{
public:
Cat() {
setAge(5);
}
};
Yet another alternative is to make Animal
's age
member protected
class Animal{
protected: // THIS
int age;
public:
Animal() : age(1) {}
void toString(){
cout << "Age: " << age << endl;
}
};
And remove Cat
's age
variable in the class definition. Despite its simplicity, this approach gives you more risk in encountering the "brittle base class" problem. Thus, I recommend the former solution as it is less prone to the said problem, and IMHO better sticks to the "write against interfaces, not implementations" principle.
Upvotes: 2
Reputation: 83225
The problem is that you are setting Cat::age
in the Cat
constructor, not the Animal::age
used by Animal::toString
.
Change the visibility of Animal::age
to protected.
class Animal {
protected:
int age;
public:
Animal() : age(1) {}
void toString(){
cout << "Age: " << age << endl;
}
};
Don't redeclare a second age
(which becomes Cat::age
). Instead, change the value of age
(Animal::age
).
class Cat : public Animal {
public:
Cat() {
age = 5;
}
};
Upvotes: 1