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();//这个是错误的编译不过。因为是私有继承,只能在类的内部调用基类的公有或是保护函数,不能在外面调用。和类内部声明的私有变量一样
下章见(以上全是个人观点,如有不当,敬请提出)