technical problem:
c++ friendship is one-way.
try protected
to give access to derived classes.
code review:
/* Polymorphic Classes and Class Friendship */
It's a good idea to avoid C /*
…*/
multiline comments in C++, because they do not nest, and some/many programmers use them to comment out code for debugging.
Instead preferentially use C++ //
(single-) line comments.
#include <iostream>
OK.
class Person{
public:
OK.
Person(char* name, int age) : Name(name), Age(age){}
The first argument should be char const*
. Without the const
you will, for example, not be able to pass a literal string, when using a C++11-conforming compiler.
char* Name;
The raw pointer here needs to match the constructor formal argument.
Effectively, the way that it's initialized as a simple copy of the constructor's pointer argument, it limits the lifetime of any Person
instance to the lifetime of the actual argument.
A std::string
is a far more flexible and unproblematic choice.
virtual void Speak(void){
std::cout << "I am a person called " << Name << std::endl;
}
Since this function is not const
, it cannot be called on a const
object.
Also, the void
is C-ism, ungood in C++.
In C it says that this function does not take any arguments. In C++ that's unnecessary, i.e. that void
is needless verbiage. Besides, C doesn't even have member functions.
virtual ~Person(void){delete this;}
Again, the void
is ungood.
The delete this
is extremely ungood in this context.
private:
int Age;
The only problem with this is failure to apply some naming convention for data members. For example, like age_
(note that the underscore then goes at the end), or like my_age
or myAge
.
};
OK.
class Boy : public Person{
OK.
friend class Person;
Meaningless, since class Person
doesn't access anything from this class.
public:
Boy(char* name, int age, Person* Friend) : Person(name, age), MyFriend(Friend){}
Again, should be char const*
. Or std::string const&
.
void Speak(void){
std::cout << "I am a boy called " << Name << ". My friend " << MyFriend->Name << "'s age is " << MyFriend->Age /* Error here */ << std::endl;
}
Here, if the compiler supports it, do add an override
in order to have the compiler check that you're really overriding a base class function, like void Speak() override {
.
In other words, ditch the void
which is needless C-ism verbiage, but do add the override
, which is very useful.
~Boy(void){delete this;}
The void
is ungood.
The delete this
is extremely ungood in this context.
private:
Person* MyFriend;
As earlier, this again limits the lifetime of a Boy
instance.
};
OK.
int main(void){
The void
is ungood.
Person* John = new Person("John", 12);
Boy* James = new Boy("James", 14, John);
Boy* Keith = new Boy("Keith", 18, James);
John->Speak();
James->Speak();
John->~Person();
So far OK.
James->~Boy();
Keith->~Boy();
Don't ever call destructors explicitly. Well, a really good experienced programmer might do that when using placement new
for allocation: it is in the language for a reason. But as a beginner, and even with a year or two professional experience, don't do that.
return (0);
Technically OK but unecessary. main
returns 0 by default.
}