c++-面向对象-多态

Source

继承补充进阶

基类指针只能访问派生类的成员变量,不能访问派生类的成员函数
赋值的机制: 编译器通过指针来访问成员变量,指针指向哪个对象就使用哪个对象的数据;编译器通过指针的类型来访问成员函数,指针属于哪个类的类型就使用哪个类的函数
见’c+±面向对象-继承’

#include <iostream>
using namespace std;

class People
{
    
      
protected:
    char *name;
    int age;
public:
    People(char *name,int age);
    void print();
};

People::People(char *name,int age)
{
    
      
    this->name=name;
    this->age=age;
}

void People::print()
{
    
      
    cout<<"class people"<<endl;
    cout<<this->name<<" "<<this->age<<endl;
}

class Student:public People
{
    
      
private:
    float score;
public:
    Student(char *name,int age,float score);
    void print();
};

Student::Student(char *name,int age,float score)
    :People(name,age)
{
    
      
    this->score=score;
}
void Student::print()
{
    
      
    cout<<"class student"<<endl;
    cout<<this->name<<" "<<this->age<<" "<<this->score;
}


int main()
{
    
      
    People *p=new People("zhangsan",10);
    /*
        class people
        zhangsan 10 
    */
    p->print();

    p=new Student("lisi",20,3.);
    /*
        class people
        lisi 20
    */
    p->print(); // 使用了派生类的成员变量,但是没有使用派生类的成员函数,仍然使用的是基类的成员函数
    return 0;
}

虚函数与多态

将上述的例子进行修改(将基类的成员函数修改为虚函数virtual function)
注意与 虚继承 的概念分开

普通继承下:
基类指针只能访问派生类的成员变量,不能访问派生类的成员函数
赋值的机制: 编译器通过指针来访问成员变量,指针指向哪个对象就使用哪个对象的数据;编译器通过指针的类型来访问成员函数,指针属于哪个类的类型就使用哪个类的函数
见’c+±面向对象-继承’

在虚函数下:
基类指针指向基类对象时就使用基类的成员(成员变量、成员函数); 指向派生类对象时就使用派生类的成员(成员变量、成员函数)
多态: 基类指针可以按照基类的方式做事,也可以按照派生类的方式做事
虚函数的唯一作用就是构成多态

多态:可以通过基类指针对所有派生类(包括直接派生和间接派生)的成员变量和成员函数进行访问,尤其是成员函数
有了多态,只需要一个指针就可以调用所有派生类的虚函数

构成多态的条件
存在继承关系
继承关系中具有同名的虚函数,且是覆盖关系(函数原型相同)
存在基类的指针,利用基类指针调用虚函数

#include <iostream>
using namespace std;

class People
{
    
      
protected:
    char *name;
    int age;
public:
    People(char *name,int age);
    virtual void print();
};

People::People(char *name,int age)
{
    
      
    this->name=name;
    this->age=age;
}

void People::print()
{
    
      
    cout<<"class people"<<endl;
    cout<<this->name<<" "<<this->age<<endl;
}

class Student:public People
{
    
      
private:
    float score;
public:
    Student(char *name,int age,float score);
    void print();
};

Student::Student(char *name,int age,float score)
    :People(name,age)
{
    
      
    this->score=score;
}
void Student::print()
{
    
      
    cout<<"class student"<<endl;
    cout<<this->name<<" "<<this->age<<" "<<this->score;
}


int main()
{
    
      
    People *p=new People("zhangsan",10);
    /*
        class people
        zhangsan 10 
    */
    p->print();

    p=new Student("lisi",20,3.);
    /*
        class student
        lisi 20
    */
    p->print(); 
    return 0;
}

例子 - 重要

#include <iostream>
using namespace std;

class School
{
    
      
public:
    virtual void print();
};

void School::print()
{
    
      
    cout<<"school class"<<endl;
}

class Grade:public School
{
    
      
public:
    void print();
};
void Grade::print()
{
    
      
    cout<<"grade class"<<endl;
}

class ClassG:public Grade
{
    
      
public:
    void print();
};
void ClassG::print()
{
    
      
    cout<<"classG class"<<endl;
}

class Student:public ClassG
{
    
      
public:
    void print();
};
void Student::print()
{
    
      
    cout<<"student class"<<endl;
}

int main()
{
    
      
    School *p=new School;
    p->print();

    Student *pstu=new Student;
    pstu->print();
    pstu->School::print();

    cout<<"******"<<endl;
    p=new Grade;
    p->print();

    p=new ClassG;
    p->print();

    p=new Student;
    p->print();

    delete p;
    return 0;
}

虚函数

函数声明必须加上virtual,函数定义加不加virtual都行
可以只将基类的中的函数声明为virtual,派生类中具有相同名字的函数自动为虚函数
在基类中定义了虚函数,如果派生类中没有定义新的函数来遮蔽此函数,那么将使用基类的虚函数
只有派生类的虚函数覆盖基类的虚函数(函数原型相同(必须完全一样))才构成多态,即可利用基类指针访问派生类函数 (基类func()、派生类func(int) 基类指针->func(1)会出错,基类指针->func()会调用基类的func())
基类和派生类的构造函数不具有虚函数的机制,不能将构造函数声明为虚函数
析构函数可以声明为虚函数,有时必须声明为虚函数

使用场景
成员函数在类的继承后有无可能被更改功能,有更改则一般声明为虚函数

class People
{
    
      
public:
    virtual void func();
    virtual void func(int a);
};
void People::func()
{
    
      
    cout<<"people class virtual func()"<<endl;
}
void People::func(int a)
{
    
      
    cout<<"people class virtual func(int)"<<endl;
}

class Student:public People
{
    
      
public:
    void func();
    void func(int a);
    void func(char *name);
};

void Student::func()
{
    
      
    cout<<"Student class virtual func()"<<endl;
}
void Student::func(int a)
{
    
      
    cout<<"Student class virtual func(int)"<<endl;
}
void Student::func(char *name)
{
    
      
    cout<<"Student class virtual func(char *name)"<<endl;
}


int main()
{
    
      
    People *p=new People;
    p->func();
    p->func(10);
    // p->func("zhangsan"); //ERROR

    p=new Student;
    p->func();
    p->func(20);
    // p->func("lisi"); // ERROR 因为不是虚函数 ,通过基类的指针只能访问从基类继承过去的成员,不能访问派生类新增的成员
    return 0;
}

例子2

#include <iostream>
using namespace std;

class People
{
    
      
protected:
    char *name;
public:
    People(char *name);
    ~People();

    virtual void func();
};

People::People(char *name)
{
    
      
    this->name=name;
}

People::~People()
{
    
      
}

void People::func()
{
    
      
    cout<<this->name<<endl;
}

class Student:public People
{
    
      
private:
    int age;
public:
    Student(char*name,int age);
    ~Student();
    void func();
};

Student::Student(char*name,int age)
    :People(name)
{
    
      
    this->age=age;
}

Student::~Student()
{
    
      
}

void Student::func()
{
    
      
    cout<<this->name<<" "<<this->age<<endl;
}

int main()
{
    
      
    People *p=new People("zhangsan");
    p->func();

    p=new Student("lisi",10);
    p->func(); // 如果不是virtual 只打印lisi 基类的成员函数访问派生类的成员变量
    return 0;
}

虚 析构函数

构造函数不能是虚函数
大部分情况下,都应该将基类的析构函数声明为虚函数

例子1

基类的指针指向了派生类对象,不对调用派生类的析构函数 --> 造成内部泄漏

因为析构函数仍然是成员函数,且该例子中是非虚函数,通过指针访问非虚函数时,编译器会根据指针的类型来确定要调用的函数,也就是说指针指向哪个类就调用哪个类的函数,除非是虚函数
基类指针只能访问派生类的成员变量,不能访问派生类的成员函数
赋值的机制: 编译器通过指针来访问成员变量,指针指向哪个对象就使用哪个对象的数据;编译器通过指针的类型来访问成员函数,指针属于哪个类的类型就使用哪个类的函数

而派生类的析构函数始终且一定会调用基类的析构函数

class People
{
    
      
protected:
    char *name;
public:
    People();
    ~People();
};

People::People()
{
    
      
    name=new char[100];
    cout<<"people class 构造"<<endl;
}

People::~People()
{
    
      
    delete[] name;
    cout<<"people class 析构"<<endl;
}

class Student:public People
{
    
      
private:
    char *str;
public:
    Student();
    ~Student();
};

Student::Student()
{
    
      
    str=new char[100];
    cout<<"student class 构造"<<endl;
}

Student::~Student()
{
    
      
    delete[] str;
    cout<<"student class 析构"<<endl;
}

int main()
{
    
      
    /*
        people class 构造
        people class 析构
    */
    People *p=new People();
    delete p;

    cout<<"*******"<<endl;
    /*
        people class 构造
        student class 构造
        student class 析构
        people class 析构
    */
    Student *pstu=new Student();
    delete pstu;

    cout<<"*******"<<endl;
    /*
        // 基类的指针指向了派生类对象,不对调用派生类的析构函数

        people class 构造
        student class 构造
        people class 析构
    */
    People *p2=new Student;
    delete p2;

    return 0;
}

例子2

修改例子1,将基类的析构函数修改为 虚析构函数,那么就可以利用多态的性质进行调用(又因为派生类的析构函数一定会调用基类的析构函数)
将基类的析构函数声明为虚函数后,派生类的析构函数自动称为虚函数

class People
{
    
      
protected:
    char *name;
public:
    People();
    virtual ~People();
};


/* == main() ==*/
/*
    people class 构造
    student class 构造
    student class 析构
    people class 析构
*/
People *p2=new Student;
delete p2;

纯虚函数与抽象类

纯虚函数没有函数体,只有函数声明,且=0,表明函数是纯虚函数
纯虚函数无法调用,无法为其分配内存空间

virtual returnType func(params)=0;

抽象类:包含纯虚函数的类为抽象类
抽象类无法实例化,无能创建实例对象
抽象类通常作为基类,让派生类去实现纯虚函数,且派生类必须实现纯虚函数才能被实例化
抽象类的作用:约束派生类的功能,并实现多态

class People
{
    
      
protected:
    float score;
public:
    People(float score);
    virtual float setdoublescore()=0;
};

People::People(float score)
{
    
      
    this->score=score;
}

class Student:public People
{
    
      
protected:
    float age;
public:
    Student(float age,float score);
    float setdoublescore();
};

Student::Student(float age,float score)
    :People(score)
{
    
      
    this->score=score;
}

float Student::setdoublescore()
{
    
      
    return this->score*2;
}

int main()
{
    
      
    Student *pstu=new Student(1.,2.);
    cout<<pstu->setdoublescore()<<endl;

    return 0;
}

虚函数进阶 - 没看