类的继承、抽象类、虚函数[C++]

1. Animal类具有吃、睡觉、呼吸的行为,Fish、Bird是Animal的一种,也有吃、睡觉、呼吸的行为,如下所示:

#include  < iostream.h >

class  Animal
{
public :
    
void  eat() { cout << " Animal eat " << endl; }
    
void  sleep() { cout << " Animal sleep " << endl; }
    
void  breathe() { cout << " Animal breathe " << endl; }
};

class  Fish
{
public :
    
void  eat() { cout << " Animal eat " << endl; }
    
void  sleep() { cout << " Animal sleep " << endl; }
    
void  breathe() { cout << " Animal breathe " << endl; }
};

class  Bird
{
public :
    
void  eat() { cout << " Animal eat " << endl; }
    
void  sleep() { cout << " Animal sleep " << endl; }
    
void  breathe() { cout << " Animal breathe " << endl; }
};

void  main()
{
    Animal animal;
    Fish fish;
    Bird bird;
    animal.eat();
    fish.sleep();
    bird.breathe();
}

 

输出结果:
Animal eat
Animal sleep
Animal breathe


2. Fish类和Animal类有相似行为时,不必重写Animal类中的方法,而采用继承,如下所示:

#include  < iostream.h >

class  Animal
{
public // 以下方法全部为公有方法
     void  eat() { cout << " Animal eat " << endl; }
    
void  sleep() { cout << " Animal sleep " << endl; }
    
void  breathe() { cout << " Animal breathe " << endl; }
};

class  Fish :  public  Animal  // 此处的public表示公有继承
{
    
// Fish类中没有添加任何方法
};

void  main()
{
    Fish fish;
    fish.eat(); 
    fish.sleep(); 
    fish.breathe(); 
}

 

输出结果:
Animal eat //表示Fish类继承了Animal类的eat方法
Animal sleep //表示Fish类继承了Animal类的sleep方法
Animal breathe //表示Fish类继承了Animal类的breathe方法


3. 访问控制主要有public, private和protected,public无访问限制,protected只能被子类访问,private只能被本类访问

#include < iostream.h >

class  Animal
{
public
    
void  eat() { cout << " Animal eat " << endl; }  // eat方法为公有方法,无访问限制
protected :
    
void  sleep() { cout << " Animal sleep " << endl; }  // sleep方法为受保护方法,只能被子类访问
private :
    
void  breathe() { cout << " Animal breathe " << endl; }  // breathe方法为私有方法,只能被本类访问
};

class  Fish :  public  Animal  // 此处的public表示公有继承
{
public :
    
void  test()
    {
        sleep(); 
// 子类可以访问父类的protected成员函数,但不能访问父类的private成员函数
        
// breathe(); 
        
// error C2248: 'breathe' : cannot access private member declared in class 'Animal'
    }
};

void  main()
{
    Animal animal;
    animal.eat();
    
// animal.sleep();
    
// error C2248: 'sleep' : cannot access protected member declared in class 'Animal'

    Fish fish; 
    
// fish.sleep();
    
// error C2248: 'sleep' : cannot access protected member declared in class 'Animal'
    fish.test();
}

 

输出结果:
Animal eat
Animal sleep

4. 类的继承访问特性

基类的访问特性类的继承特性子类的访问特性

public, protected, private

publicpublic, protected, no access

public, protected, private

protected

protected, protected, no access

public, protected, private

private

private, private, no access

5. 基类与派生类的构造与析构顺序为基类先构造派生类后构造,析构则相反

#include  < iostream.h >

class  Animal
{
public
    Animal() { cout
<< " Animal Constructor! " << endl; }
    
~ Animal() { cout << " Animal Deconstructor! " << endl; }
    
void  eat() { cout << " Animal eat " << endl; }
    
void  sleep() { cout << " Animal sleep " << endl; }
    
void  breathe() { cout << " Animal breathe " << endl; }
};

class  Fish :  public  Animal 
{
public :
    Fish() { cout
<< " Fish Constructor! " << endl; }
    
~ Fish() { cout << " Fish Deconstructor! " << endl; }
};

void  main()
{
    Fish fish; 
}

 

输出结果:
Animal Constructor!
Fish Constructor!
Fish Deconstructor!
Animal Deconstructor!

6. 构造类Fish的对象时,先根据父类的缺省构造函数(不带参数的构造函数)或不带参数的构造函数构造父类的对象。只要类中显式提供了构造函数编译器就不会再提供缺省构造函数。以下代码中由于基类的构造函数带有参数,在main中构造Fish的对象时找不到合适的基类构造函数。

#include < iostream.h >

class  Animal
{
public
    Animal(
int  height,  int  weight) { cout << " Animal Constructor! " << endl; }
    
// error C2512: 'Animal' : no appropriate default constructor available
     ~ Animal() { cout << " Animal Deconstructor! " << endl; }
    
void  eat() { cout << " Animal eat " << endl; }
    
void  sleep() { cout << " Animal sleep " << endl; }
    
void  breathe() { cout << " Animal breathe " << endl; }
};

class  Fish :  public  Animal 
{
public :
    Fish() { cout
<< " Fish Constructor! " << endl; }
    
~ Fish() { cout << " Fish Deconstructor! " << endl; }
};

void  main()
{
    Fish fish; 
}

 

7. 我们想在构造Animal的时候传递身高和体重,怎么办?解决办法是在子类当中向基类的带参数的构造函数传递参数。这种方法也可用来初始化类中的常量成员,请参考下列代码:

#include < iostream.h >

class  Animal
{
public
    Animal(
int  height,  int  weight)
    {
        cout
<< " Animal Constructor! " << endl; 
    }
    
~ Animal() { cout << " Animal Deconstructor! " << endl; }
    
void  eat() { cout << " Animal eat " << endl; }
    
void  sleep() { cout << " Animal sleep " << endl; }
    
void  breathe() { cout << " Animal breathe " << endl; }
};

class  Fish :  public  Animal 
{
public :
    Fish() : Animal(
400 300 ), _number( 0 // 在子类当中向基类的带参数的构造函数传递参数
    {
        cout
<< " Fish Constructor! " << endl; 
    }
    
~ Fish() { cout << " Fish Deconstructor! " << endl; }
private :
    
int   const  _number;
};

void  main()
{
    Fish fish; 
}

 

8. 考虑到Fish与一般Animal的breathe方式不同,在Fish类中也写了一个同样的breathe,其返回值,参数,函数名与Animal类中的一模一样,这叫做函数的覆盖。函数的覆盖是发生在父类与子类之间的,而函数的重载是发生在一个类中的。函数覆盖的好处就是在子类中可以把从父类继承来的行为,保留其名称,重写其内容,即覆盖掉。如果希望仍然获取基类的被覆盖方法,可用类名加::调用。如下所示:

#include < iostream.h >

class  Animal
{
public
    Animal(
int  height,  int  weight) {}
    
~ Animal() {}
    
void  breathe()
    {
        cout
<< " Animal breathe " << endl;
    }
};

class  Fish :  public  Animal 
{
public :
    Fish() : Animal(
400 300 ), _number( 0 ) {}
    
~ Fish() {}
    
void  breathe()  // 子类覆盖父类的同名函数
    {
        Animal::breathe(); 
// Fish还希望保留一般Animal的呼吸方式时,用类加作用域符::
        cout << " Fish bubble " << endl;  // Fish的呼吸方式与一般Animal是不同的
    }
private :
    
int   const  _number;
};

void  main()
{
    Fish fish; 
    fish.breathe();
}

 

输出结果:
Animal breathe
Fish bubble

9. 本例通过一个全局函数来了解Animal和Fish的内存结构。由于创建Fish对象时,先创建的是Animal对象,实际上两个对象的起始地址是一样的。所以下列代码输出的是Animal的breathe,animal=&fish进行了转换,根据强制转换原则是可以的

#include < iostream.h >

class  Animal
{
public
    Animal(
int  height,  int  weight) {}
    
~ Animal() {}
    
void  breathe()
    {
        cout
<< " Animal breathe " << endl;
    }
};

class  Fish :  public  Animal 
{
public :
    Fish() : Animal(
400 300 ) {}
    
~ Fish() {}
    
void  breathe()
    {
        cout
<< " Fish bubble " << endl;
    }
};

void  fn(Animal  * pAn)  // 全局函数
{
    pAn
-> breathe();
}

void  main()
{
    Fish fish; 
    Animal 
* animal;
    animal
=& fish;
    fn(animal);
}

 

输出结果:
Animal breathe

10. 如果想输出Fish的breathe呢?我们可以把基类中的breathe变成virtual breathe。
  多态性:当C++编译器在编译的时候,发现Animal类的breathe函数为虚函数,这个时候C++就采用迟绑定技术,在运   
  行时根据对象的(这里是Fish类对象的地址)来确定调用哪一个函数。传递的必须是子类的地址。如下所示,当子没有
  breathe方法时,就调用基类的virtual breathe方法

#include < iostream.h >

class  Animal
{
public
    Animal(
int  height,  int  weight) {}
    
~ Animal() {}
    
virtual   void  breathe()  // 虚函数
    {
        cout
<< " Animal breathe " << endl;
    }
};

class  Fish :  public  Animal 
{
public :
    Fish() : Animal(
400 300 ) {}
    
~ Fish() {}
    
void  breathe()  // 如果子类中没有breathe方法,则调用父类中的virtual breathe
    {
        cout
<< " Fish bubble " << endl;
    }
};

void  fn(Animal  * pAn)
{
    pAn
-> breathe();
}

void  main()
{
    Fish fish; 
    Animal 
* animal;
    animal
=& fish;  // 传递的是子类的地址
    fn(animal);
}

 

输出结果:
Fish bubble

11. C++中还有一种函数叫纯虚函数,是我们在设计基类的时候不好确定将来的行为具体应该表现为什么行为,但是必须的。含有纯虚函数的类叫抽象类。抽象类不能实例化它的对象,只能为它的派生类服务。如果子类没有实现父类的纯虚函数,则子类也变成抽象类,它也不能实例化对象。

#include < iostream.h >

class  Animal
{
public
    Animal(
int  height,  int  weight) {}
    
~ Animal() {}
    
virtual   void  breathe() = 0 // 纯虚函数,只有定义,没有内容
};

class  Fish :  public  Animal 
{
public :
    Fish() : Animal(
400 300 ) {}
    
~ Fish() {}
    
void  breathe()  // 在子类中实现父类的纯虚函数
    {
        cout
<< " Fish bubble " << endl;
    }
};

void  fn(Animal  * pAn)
{
    pAn
-> breathe();
}

void  main()
{
    Fish fish; 
    Animal 
* animal;
    animal
=& fish;
    fn(animal);
}


输出结果:
Fish bubble

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值