21天学C++(十二)继承

完整目录、平台简介、安装环境及版本:参考《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!
请按任意键继续. . .

发表回复