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

来源:互联网 发布:xp bitcomet端口阻塞 编辑:程序博客网 时间:2024/05/21 19:36

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(
400300), _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(
400300), _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(
400300) {}
    
~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(
400300) {}
    
~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(
400300) {}
    
~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

原创粉丝点击