完整目录、平台简介、安装环境及版本:参考《21天学C++ 概览》
十二、继承
12.1 继承和派生
12.1.1 继承和派生
派生是is-a 关系,如爬行动物是动物;
- 基类:如动物是爬行动物和哺乳动物的基类,哺乳动物又是马和狗的基类;
- 派生类:如哺乳动物和爬行动物是动物的派生类,马和狗是哺乳动物的派生类;
- 基类:具有派生类的共性;
- 派生类:在基类的基础上添加自己的特性;
基类可以有多个派生类,每个派生类可以添加自己的特性;
12.1.2 格式/语法
class Dog:public Mammal{};
声明一个类,然后在其后添加一个冒号:来表示它是从那个类中派生而来、派生类型(public,private,protected)及基类名字;
12.1.3 public、private、protected访问可见性
公有继承public
- public->pulibc,protected->protected,private不可访问;
- 派生类成员函数可以访问基类的public和protected成员,不能访问基类private成员;
- 派生类对象只能访问基类的public成员;
私有继承private
- public->private,protected->private,private不可访问;
- 派生类成员函数可以访问基类的public和protected成员,不能访问基类private成员;
- 派生类对象不能访问任何成员;
保护继承protected
- public->protected,protected->protected,private不可访问;
- 派生类成员函数可以访问基类的public和protected成员,不能访问基类private成员;
- 派生类对象不能访问任何成员;
总结
- 派生类成员函数均可以访问基类的public和protected成员,但不能访问基类private成员;
- 派生类对象,只有公有继承的派生类对象能访问基类的public成员;
12.1.4 实例
#include <iostream>
using std::cout;
using std::endl;
enum BREED { GOLDEN, CAIRN, DANDIE, SHETLAND, DOBERMAN, LAB };
class Mammal
{
public:
// constructors
Mammal():itsAge(2), itsWeight(5){}
~Mammal(){}
//accessors
int GetAge() const { return itsAge; }
void SetAge(int age) { itsAge = age; }
int GetWeight() const { return itsWeight; }
void SetWeight(int weight) { itsWeight = weight; }
//Other methods
void Speak()const { cout << "Mammal sound!\n"; }
void Sleep()const { cout << "shhh. I'm sleeping.\n"; }
protected:
int itsAge;
int itsWeight;
};
class Dog : public Mammal
{
public:
// Constructors
Dog():itsBreed(GOLDEN){}
~Dog(){}
// Accessors
BREED GetBreed() const { return itsBreed; }
void SetBreed(BREED breed) { itsBreed = breed; }
// Other methods
void WagTail() const { cout << "Tail wagging...\n"; }
void BegForFood() const { cout <<"Begging for food...\n"<<itsWeight;}
private:
BREED itsBreed;
};
int main()
{
Dog Fido;
Fido.Speak();
Fido.WagTail();
cout << "Fido is " << Fido.GetAge() << " years old" << endl;
Fido.BegForFood();
return 0;
}
执行结果如下:
Mammal sound!
Tail wagging...
Fido is 2 years old
Begging for food...
5请按任意键继续. . .
12.2 构造函数和析构函数
12.2.1 调用顺序
创建Fido对象时,先调用基类Mammal{构造函数,创建Mammal对象,然后调用Dog构造函数完成Dog对象的创建;
析构Fido对象时,先调用Dog的析构函数,然后调用Mammal对象的析构函数;
12.2.2 向基类构造函数传递参数
#include <iostream>
using namespace std;
enum BREED { GOLDEN, CAIRN, DANDIE, SHETLAND, DOBERMAN, LAB };
class Mammal
{
public:
// constructors
Mammal();
Mammal(int age);
~Mammal();
//accessors
int GetAge() const { return itsAge; }
void SetAge(int age) { itsAge = age; }
int GetWeight() const { return itsWeight; }
void SetWeight(int weight) { itsWeight = weight; }
//Other methods
void Speak() const { cout << "Mammal sound!\n"; }
void Sleep() const { cout << "shhh. I'm sleeping.\n"; }
protected:
int itsAge;
int itsWeight;
};
Mammal::Mammal():
itsAge(1),
itsWeight(5)
{
cout << "Mammal constructor..." << endl;
}
Mammal::Mammal(int age):
itsAge(age),
itsWeight(5)
{
cout << "Mammal(int) constructor..." << endl;
}
Mammal::~Mammal()
{
cout << "Mammal destructor..." << endl;
}
class Dog : public Mammal
{
public:
// Constructors
Dog();
Dog(int age);
Dog(int age, int weight);
Dog(int age, BREED breed);
Dog(int age, int weight, BREED breed);
~Dog();
// Accessors
BREED GetBreed() const { return itsBreed; }
void SetBreed(BREED breed) { itsBreed = breed; }
// Other methods
void WagTail() const { cout << "Tail wagging...\n"; }
void BegForFood() const { cout << "Begging for food...\n"; }
private:
BREED itsBreed;
};
Dog::Dog():Mammal(),itsBreed(GOLDEN)
{
cout << "Dog constructor..." << endl;
}
Dog::Dog(int age):Mammal(age),itsBreed(GOLDEN)
{
cout << "Dog(int) constructor..." << endl;
}
Dog::Dog(int age, int weight):Mammal(age),itsBreed(GOLDEN)
{
itsWeight = weight;
cout << "Dog(int, int) constructor..." << endl;
}
Dog::Dog(int age, int weight, BREED breed):Mammal(age),itsBreed(breed)
{
itsWeight = weight;
cout << "Dog(int, int, BREED) constructor..." << endl;
}
Dog::Dog(int age, BREED breed):Mammal(age),itsBreed(breed)
{
cout << "Dog(int, BREED) constructor..." << endl;
}
Dog::~Dog()
{
cout << "Dog destructor..." << endl;
}
int main()
{
Dog Fido;
Dog rover(5);
Dog buster(6,8);
Dog yorkie (3,GOLDEN);
Dog dobbie (4,20,DOBERMAN);
Fido.Speak();
rover.WagTail();
cout << "Yorkie is " << yorkie.GetAge()
<< " years old" << endl;
cout << "Dobbie weighs ";
cout << dobbie.GetWeight() << " pounds" << endl;
return 0;
}
执行结果如下:
Mammal constructor...
Dog constructor...
Mammal(int) constructor...
Dog(int) constructor...
Mammal(int) constructor...
Dog(int, int) constructor...
Mammal(int) constructor...
Dog(int, BREED) constructor...
Mammal(int) constructor...
Dog(int, int, BREED) constructor...
Mammal sound!
Tail wagging...
Yorkie is 3 years old
Dobbie weighs 20 pounds
Dog destructor...
Mammal destructor...
Dog destructor...
Mammal destructor...
Dog destructor...
Mammal destructor...
Dog destructor...
Mammal destructor...
Dog destructor...
Mammal destructor...
请按任意键继续. . .
12.3 覆盖函数
12.3.1 函数覆盖
定义:当派生类与基类成员函数签名一致,但派生类用新的方法实现,这就成为函数覆盖;
签名:函数名、参数列表和可能用到的关键字const,但是不包括返回类型;
函数重载:函数名相同,参数列表不同,返回类型没要求。
#include <iostream>
using std::cout;
enum BREED { GOLDEN, CAIRN, DANDIE, SHETLAND, DOBERMAN, LAB };
class Mammal
{
public:
// constructors
Mammal() { cout << "Mammal constructor...\n"; }
~Mammal() { cout << "Mammal destructor...\n"; }
//Other methods
void Speak()const { cout << "Mammal sound!\n"; }
void Sleep()const { cout << "shhh. I'm sleeping.\n"; }
protected:
int itsAge;
int itsWeight;
};
class Dog : public Mammal
{
public:
// Constructors
Dog(){ cout << "Dog constructor...\n"; }
~Dog(){ cout << "Dog destructor...\n"; }
// Other methods
void WagTail() const { cout << "Tail wagging...\n"; }
void BegForFood() const { cout << "Begging for food...\n"; }
void Speak() const { cout << "Woof!\n"; }
private:
BREED itsBreed;
};
int main()
{
Mammal bigAnimal;
Dog fidoFido;
bigAnimal.Speak();
fidoFido.Speak();
return 0;
}
执行结果如下:
Mammal constructor...
Mammal constructor...
Dog constructor...
Mammal sound!
Woof!
Dog destructor...
Mammal destructor...
Mammal destructor...
请按任意键继续. . .
12.3.2 隐藏基类方法
一旦你覆盖了任一个重载的方法,那么对这个方法的所有其他覆盖均被隐藏了。如果不想让他们隐藏,你必须把它们全部覆盖;
#include <iostream>
using std::cout;
class Mammal
{
public:
void Move() const { cout << "Mammal move one step.\n"; }
void Move(int distance) const
{
cout << "Mammal move ";
cout << distance <<" steps.\n";
}
protected:
int itsAge;
int itsWeight;
};
class Dog : public Mammal
{
public:
// You might receive a warning that you are hiding a function!
void Move() const { cout << "Dog move 5 steps.\n"; }
};
int main()
{
Mammal bigAnimal;
Dog Fido;
bigAnimal.Move();
bigAnimal.Move(2);
Fido.Move();
//Fido.Move(10);//error C2660: “Dog::Move”: 函数不接受 1 个参数
return 0;
}
执行结果如下:
Mammal move one step.
Mammal move 2 steps.
Dog move 5 steps.
请按任意键继续. . .
12.3.3 调用基方法
如果覆盖了基方法,通过完全限定这个方法的名字仍旧可以调用它;
#include <iostream>
using namespace std;
class Mammal
{
public:
void Move() const { cout << "Mammal move one step\n"; }
void Move(int distance) const
{
cout << "Mammal move " << distance;
cout << " steps." << endl;
}
protected:
int itsAge;
int itsWeight;
};
class Dog : public Mammal
{
public:
void Move()const;
};
void Dog::Move() const
{
cout << "In dog move...\n";
Mammal::Move(3);
}
int main()
{
Mammal bigAnimal;
Dog Fido;
bigAnimal.Move();
bigAnimal.Move(2);
Fido.Move();
Fido.Mammal::Move(6);
return 0;
}
执行结果如下:
Mammal move one step
Mammal move 2 steps.
In dog move...
Mammal move 3 steps.
Mammal move 6 steps.
请按任意键继续. . .
12.4 虚函数
12.4.1 C++扩展其多态性,允许把派生类对象赋给指向基类的指针
Mammal *pMammal = new Dog;
在堆上创建一个新对象,并返回这个对象的指针,而他又将这个指针赋给指向Mammal的指针;
可以利用这个指针调用Mammal类中的任何一个方法;
如果想调用那些在Dog中被覆盖的方法,虚函数就可以做到这一点;
一旦一个函数是虚函数,如果派生类对其进行了覆盖,它仍是一个虚函数
#include <iostream>
using std::cout;
class Mammal
{
public:
Mammal():itsAge(1) { cout << "Mammal constructor...\n"; }
virtual ~Mammal() { cout << "Mammal destructor...\n"; }
void Move() const { cout << "Mammal move one step\n"; }
virtual void Speak() const { cout << "Mammal speak!\n"; }
protected:
int itsAge;
};
class Dog : public Mammal
{
public:
Dog() { cout << "Dog Constructor...\n"; }
virtual ~Dog() { cout << "Dog destructor...\n"; }
void WagTail() { cout << "Wagging Tail...\n"; }
void Speak()const { cout << "Woof!\n"; }
void Move()const { cout << "Dog moves 5 steps...\n"; }
};
int main()
{
Mammal *pDog = new Dog;
pDog->Move();
pDog->Speak();
delete pDog;
return 0;
}
执行结果如下:
Mammal constructor...
Dog Constructor...
Mammal move one step
Woof!
Dog destructor...
Mammal destructor...
请按任意键继续. . .
12.4.2 动态编译
#include <iostream>
using namespace std;
class Mammal
{
public:
Mammal():itsAge(1) { }
virtual ~Mammal() { }
virtual void Speak() const { cout << "Mammal speak!\n"; }
protected:
int itsAge;
};
class Dog : public Mammal
{
public:
void Speak()const { cout << "Woof!\n"; }
};
class Cat : public Mammal
{
public:
void Speak()const { cout << "Meow!\n"; }
};
class Horse : public Mammal
{
public:
void Speak()const { cout << "Winnie!\n"; }
};
class Pig : public Mammal
{
public:
void Speak()const { cout << "Oink!\n"; }
};
int main()
{
Mammal* theArray[5];
Mammal* ptr;
int choice, i;
for ( i = 0; i<5; i++)
{
cout << "(1)dog (2)cat (3)horse (4)pig: ";
cin >> choice;
switch (choice)
{
case 1: ptr = new Dog;
break;
case 2: ptr = new Cat;
break;
case 3: ptr = new Horse;
break;
case 4: ptr = new Pig;
break;
default: ptr = new Mammal;
break;
}
theArray[i] = ptr;
}
for (i=0;i<5;i++)
theArray[i]->Speak();
return 0;
}
执行结果如下:
(1)dog (2)cat (3)horse (4)pig: 1
(1)dog (2)cat (3)horse (4)pig: 2
(1)dog (2)cat (3)horse (4)pig: 3
(1)dog (2)cat (3)horse (4)pig: 4
(1)dog (2)cat (3)horse (4)pig: 5
Woof!
Meow!
Winnie!
Oink!
Mammal speak!
请按任意键继续. . .
12.4.3 不能从基类访问派生类中的函数
除非基类指针指向派生类对象;
12.4.4 slicing切片
虚函数的魔力只对指针和引用有效,对按值传递对象不允许调用虚函数;
当基类指针指向派生类对象,然后通过值传递后,就被切片成只剩下基类部分;
#include <iostream>
using namespace std;
class Mammal
{
public:
Mammal():itsAge(1) { }
virtual ~Mammal() { }
virtual void Speak() const { cout << "Mammal speak!\n"; }
protected:
int itsAge;
};
class Dog : public Mammal
{
public:
void Speak()const { cout << "Woof!\n"; }
};
class Cat : public Mammal
{
public:
void Speak()const { cout << "Meow!\n"; }
};
void ValueFunction (Mammal);
void PtrFunction (Mammal*);
void RefFunction (Mammal&);
int main()
{
Mammal* ptr=0;
int choice;
while (1)
{
bool fQuit = false;
cout << "(1)dog (2)cat (0)Quit: ";
cin >> choice;
switch (choice)
{
case 0: fQuit = true;
break;
case 1: ptr = new Dog;
break;
case 2: ptr = new Cat;
break;
default: ptr = new Mammal;
break;
}
if (fQuit==true)
break;
ValueFunction(*ptr);
PtrFunction(ptr);
RefFunction(*ptr);
}
return 0;
}
void ValueFunction (Mammal MammalValue)
{
MammalValue.Speak();
}
void PtrFunction (Mammal * pMammal)
{
pMammal->Speak();
}
void RefFunction (Mammal & rMammal)
{
rMammal.Speak();
}
执行结果如下:
(1)dog (2)cat (0)Quit: 1
Mammal speak!
Woof!
Woof!
(1)dog (2)cat (0)Quit: 2
Mammal speak!
Meow!
Meow!
(1)dog (2)cat (0)Quit: 0
请按任意键继续. . .
12.5 虚析构函数
如果你类中任何一个函数是虚函数,那么析构函数也应该是虚函数
将派生类对象的指针传递个基类对象时,删除该对象,需要通过虚析构函数进行正确的销毁;
#include <iostream>
using std::cout;
class Mammal
{
public:
Mammal() { cout << "Mammal constructor...\n"; }
virtual ~Mammal() { cout << "Mammal destructor...\n"; }
};
class Dog : public Mammal
{
public:
Dog() { cout << "Dog Constructor...\n"; }
virtual ~Dog() { cout << "Dog destructor...\n"; }
};
int main()
{
Mammal *pDog = new Dog;
delete pDog;
return 0;
}
执行结果如下:
Mammal constructor...
Dog Constructor...
Dog destructor...
Mammal destructor...
请按任意键继续. . .
12.6 虚复制构造函数
构造函数不能是虚函数,也就不存在虚复制构造函数;
有时需要拷贝派生类对象,并返回基对象指针,如Clone()方法;
#include <iostream>
using namespace std;
class Mammal
{
public:
Mammal():itsAge(1) { cout << "Mammal constructor...\n"; }
virtual ~Mammal() { cout << "Mammal destructor...\n"; }
Mammal (const Mammal & rhs);
virtual void Speak() const { cout << "Mammal speak!\n"; }
virtual Mammal* Clone() { return new Mammal(*this); }
int GetAge()const { return itsAge; }
protected:
int itsAge;
};
Mammal::Mammal (const Mammal & rhs):itsAge(rhs.GetAge())
{
cout << "Mammal Copy Constructor...\n";
}
class Dog : public Mammal
{
public:
Dog() { cout << "Dog constructor...\n"; }
virtual ~Dog() { cout << "Dog destructor...\n"; }
Dog (const Dog & rhs);
void Speak()const { cout << "Woof!\n"; }
virtual Mammal* Clone() { return new Dog(*this); }
};
Dog::Dog(const Dog & rhs):
Mammal(rhs)
{
cout << "Dog copy constructor...\n";
}
class Cat : public Mammal
{
public:
Cat() { cout << "Cat constructor...\n"; }
~Cat() { cout << "Cat destructor...\n"; }
Cat (const Cat &);
void Speak()const { cout << "Meow!\n"; }
virtual Mammal* Clone() { return new Cat(*this); }
};
Cat::Cat(const Cat & rhs):
Mammal(rhs)
{
cout << "Cat copy constructor...\n";
}
enum ANIMALS { MAMMAL, DOG, CAT};
const int NumAnimalTypes = 3;
int main()
{
Mammal *theArray[NumAnimalTypes];
Mammal* ptr;
int choice, i;
for ( i = 0; i<NumAnimalTypes; i++)
{
cout << "(1)dog (2)cat (3)Mammal: ";
cin >> choice;
switch (choice)
{
case DOG: ptr = new Dog;
break;
case CAT: ptr = new Cat;
break;
default: ptr = new Mammal;
break;
}
theArray[i] = ptr;
}
Mammal *OtherArray[NumAnimalTypes];
for (i=0;i<NumAnimalTypes;i++)
{
theArray[i]->Speak();
OtherArray[i] = theArray[i]->Clone();
}
for (i=0;i<NumAnimalTypes;i++)
OtherArray[i]->Speak();
return 0;
}
执行结果如下:
(1)dog (2)cat (3)Mammal: 1
Mammal constructor...
Dog constructor...
(1)dog (2)cat (3)Mammal: 2
Mammal constructor...
Cat constructor...
(1)dog (2)cat (3)Mammal: 3
Mammal constructor...
Woof!
Mammal Copy Constructor...
Dog copy constructor...
Meow!
Mammal Copy Constructor...
Cat copy constructor...
Mammal speak!
Mammal Copy Constructor...
Woof!
Meow!
Mammal speak!
请按任意键继续. . .