第11章实现继承

1.派生。在已有类的基础上添加了新功能的类被成为从原来的类派生而来。原来的类是新类的基类,派生类是基类的超集

2.例子:

#include <iostream>

using std:cout;

using std::endl;

enum BREED{GOLDEN, CAIRN, DANDIE, SHETLAND, DOBERMAN, LAB};

class Mammal//声明一个类Mammal

{

public://公共属性,下面的方法所有的外部类都可以访问

Mamal():itsAge(2),itsWeight(5){}//构造函数,在实例化的时候运行

~Mammal(){}//析构函数,在对象销毁时调用

int GetAge()const{return itsAge;}

void SetAge(int age){itsAge=age;}

int GeWeight()const{return itsWeight;}

void SetWeight(int weight){itsWeight=weight;}

void Speak()const{cout <<"mamal"<<endl;}

void Sleep()const{cout << "shhh"<<endl;}

 

protected://保护对象,只有类自己或派生类能访问

int itsAge;

int itsWeight;

};//注意必须有分号

class Dog: public Mammal//定义一个类dog,并公共继承Mammal类,也就是Mammal中的公共和保护的成员dog都可以用和访问

{

public:

Dog():itsBreed(GOLDEN){}

~Dog(){}

BREED GetBreed()const{return itsBreed;}

void SetBreed(BREED breed){itsBreed=breed;}

void WagTail()const{cout<<"tail"<<endl;}

void BegForFood()const{cout <<"begging"<<endl;}

private:

BREED itsBreed;

};

int main()

{

Dog Fido;

Fido.Speak();//调用Fido的speak函数,由于Fido是dog的实例化,所以发现dog中没有speak,那就调用基类Mammal的speak函数

Fido.WagTail();//调用Fido的wagtail函数

cout<<"Fido is"<Fido.GetAge()<<" years old"<<endl;

return 0;

}

输出结果:

mamal

tail

Fido is 2 years old

3.构造函数和析构函数。每个类都有至少一个构造函数和唯一的一个析构函数。如果你没有定义,那么程序会给你定义默认的构造函数和析构函数。构造函数的格式是与类名相同,没有返回值和类型,可以有参数。析构函数的格式是以~(波浪号)开头,然后与类名相同,但不能有返回值和参数。例如

class Mammal

{

public:

Mammal();

Mammal(int age);

Mammal(float age);

~Mammal();

protected:

int age;

float agef;

};

Mammal::Mammal()//1

{

age=1;

}

Mammal::Mammal(int age)//2

{

this.age=age;

}

Mammal::Mammal(float age):age(4)//3. 调用这个构造函数时,会给age赋值3,

{

agef=age;

}

Mammal::~Mammal()//4

{

}

//实例化

Mammal Mybaby1;//这时就会调用1处的构造函数,里面的age=1

Mammal Mybaby2(5);//这里就会调用2出的构造函数,里面的age=5

Mammal Mybaby3(6.4)//这里就会调用3出的构造函数,里面的agef=6.4,并且age=4

//当程序结束,销毁时,Mybaby1,Mybaby2,Mybaby3都会销毁,同样都会调用一次4处的析构函数。

4.覆盖。如果派生类中创建一个方法,它的参数列表和方法名字与基类相同,那么就叫做覆盖,返回值可以不同。

class Mammal

{

public:

void speak()const{cout << "sound" << endl;

}

class Dog : public Mammal

{

public:

void speak()const{cout << "woof" << endl;

}

Mammal mammal;

Dog dog;

mammal.speak();//这里输出的是sound

dog.speak();//这里输出的是woof

5.隐藏。上面dog的speak方法覆盖了mammal的speak方法,我们也可以说是隐藏。如果基类有重载的方法,覆盖时就要注意了。比如下面的例子

class Mammal

{

public:

void move()const{cout << "mammal move one step" << endl;}

void move(int distance){cout << "mamal move"<<distance<<" steps"<< endl;}

}

class Dog:public Mammal

{

public:

void move()const{cout <"dog move one step"<< endl;}

}

Mammal mammal;

Dog dog;

mammal.move();

mammal.move(3);

dog.move();

//dog.move(4);

上面的例子中Mammal中有个move函数,一个是没有参数,一个是有一个int参数。但是dog中只有一个没有参数的move函数。当你用Mammal的对象调用带参数的move函数是可以的,但是Dog的对象不可以,因为子类如果覆盖了一个基类的函数,那么基类的与这个函数相关的所有重载函数都会被隐藏。

所以覆盖的时候要注意,需要的话就把所有的重载函数都覆盖一遍,况且覆盖的时候注意const,这个也是特征标,和参数函数名一样,必须一样。

6.虚方法。

class Mammal

{

public:

void move() const{cout << " Mammal move"<< endl;}

virtual void speak() const{cout << "mammal speak" << endl;}

};

class Dog:public Mammal

{

public:

void move() const{cout << "dog move" << endl;}

void speak() cont{cout << "dog speak" << endl;}

};

Mammal *pDog=new Dog;

pDog->move();//输出是mammal move

pDog->speak();//输出是dog speak

上面在Mammal中定义了一个虚函数speak,其关键字是前面的virtual。然后实例化了一个Dog类的对象指针,但是是赋值给了一个Mammal的对象指针。这样,调用pDog里面的方法时,如果不是虚函数,那么就会先调用Mammal的相应的方法,如果没有再调用Dog的,如果是虚函数,则直接调用Dog的。

虚函数的作用就是,如果你有多个类,比如Cat, Pig, Monkey等分别继承了Mammal,那么可以直接调用一个Mammal的实例化对象的指针的speak函数,然后把不同的类的对象赋值给它,这个函数就会根据不同赋值的类,来调用不同的方法了。

比如接上面代码

class Cat:public Mammal

{

public:

void move() const{cout << "cat move" << endl;}

void speak() cont{cout << "cat speak" << endl;}

};

Mammal *pMam=new Dog;

pMam->speak();//输出结果 dog speak

pMam=new Cat;//这里仅仅是为了实验,并不提倡直接这样做,因为dog的对象没有释放,成为了野指针

pMam->speak();//输出结果cat speak

7.切除.

class Mammal

{

public:

virtual void speak() const{cout << "mammal speak" << endl;}

};

class Dog:public Mammal

{

public:

void speak() cont{cout << "dog speak" << endl;}

};

void valudf(Mammal mv)

{

mv.speak();

}

void ptrf(Mammal * pm)

{

pm.speak();

}

void reff(Mammal & rm)

{

rm.speak();

}

Mammal * ptr=0;

ptr=new Dog;

valudf(*ptr);//输出 mammal speak

ptrf(ptr);//输出 dog speak

reff(*ptr);//输出 dog speak

上面之所以是这样,是因为,传递指针或是引用时,程序能够识别这个ptr的类型到底是Mammal还是dog,并根据虚函数来调用Dog的相应函数。而如果传递值,ptr里面的Dog的部分被截断,扔掉,然后赋值给了mv,所以在valudf函数内就只有Mammal的对象,所以调用了Mammal的函数

8.虚析构函数。如果雷轰任何一个函数是虚函数,那么析构函数也应该是虚函数。因为,如果用基类的指针来调用派生类的对象,那么当对象销毁时,我们希望的是调用派生类的析构函数,所以析构函数也应该是虚函数,不然就调用的是基类的析构函数。

9.不要把构造函数声明为虚方法。使用虚方法会创建对应的虚表,增加相应的开销

10私有继承。如果一个类只想使用基类的方法,不想对其做任何改变,可以使用私有继承。

class em

{

pulbic:

void sm()

{

cout << "yes" << endl;

}

};

class fan :private em

{

public:

sfan()

{

sm();

}

};

fan mfan;

mfn.sfan();

mfn.sm();//这个是错误的编译不过。因为是私有继承,只能在类的内部调用基类的公有或是保护函数,不能在外面调用。和类内部声明的私有变量一样

下章见(以上全是个人观点,如有不当,敬请提出)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章