父类指针与子类指针,开发中经常用到(继承方式必须是public,否则父类指针是不能指向子类对象的,编译器会报错)

class Person {
public:
	int m_age;
};

class Student : public Person {
public:
	int m_score;
};

父类指针可以指向子类对象,这样是安全的

子类指针不可以指向父类对象,这样是不安全的 ```swift int main() { // 学生是一个人(编译通过) Person *stu = new Student(); stu->m_age = 10;

// 如果不强制转换(Student *),编译器都不会通过
// 这样是不安全的,因为父类对象只占用4个字节
Student *p = (Student *) new Person();
p->m_age = 10;
p->m_score = 20;

getchar();
return 0; } ``` # 面向对象的3大特性: - 封装 - 继承 - 多态

多态

默认情况下,编译器只会根据指针类型调用对应的函数,不存在多态

多态是面向对象非常重要的一个特性

同一操作作用于不同的对象,可以有不同的解释,产生不同的执行结果

在运行时,可以识别出真正的对象类型,调用对应子类中的函数

多态的要素

默认情况,父类成员函数run()不加virtual

class Animal {
public:
// virtual
    void run() {
        cout << "Animal::run()" << endl;
    }
};

class Cat : public Animal {
public:
    void run() {
        cout << "Cat::run()" << endl;
    }
};

class Dog : public Animal {
public:
    void run() {
        cout << "Dog::run()" << endl;
    }
};

class Pig : public Animal {
public:
    void run() {
        cout << "Pig::run()" << endl;
    }
};

int main() {

    Animal *animal = new Pig();
    // 打印是"Animal::run()"而不是"Pig::run()"
    animal->run();

    getchar();
    return 0;
}

默认以上写法,打印是”Animal::run()”而不是”Pig::run()”,这是编译器的特性,指针是什么类型,就会调用指针对象的某个方法

如果想实现多态,执行真正对象的某个方法,只需要将父类的成员函数前加virtual关键字,子类成员函数前virtual可以省略,也可以写上

如果继承关系如下ErHa-> Dog->Animal,关键字virtual只在Dog的成员函数前面有

class Animal {
public:

    void run() {
		cout << "Animal::run()" << endl;
	}
};

class Dog : public Animal {
public:
	virtual void run() {
		cout << "Dog::run()" << endl;
	}
};

class ErHa : public Dog {
public:
	void run() {
		cout << "ErHa::run()" << endl;
	}
};

那么,所有Animal修饰的指针,只会调用Animal的成员函数 以Dog开始,才会实现多态

int main() {
    
	Animal *animal0 = new Animal();
	animal0->run(); // "Animal::run()"

	Animal *animal1 = new Dog();
	animal1->run(); // "Animal::run()"

	Animal *animal2 = new ErHa();
	animal2->run(); // "Animal::run()"

	Dog *dog0 = new Dog();
	dog0->run(); // "Dog::run()"

    // 多态要素:
    // 1.子类重写父类的成员函数(override) 
    // 2.父类指针指向子类对象 
    // 3.利用父类指针调用重写的成员函数
	Dog *dog1 = new ErHa();
	dog1->run(); // "ErHa::run()"

	getchar();
	return 0;
}

虚函数

知识点回顾:

重写:子类重写(覆盖)父类的方法 重载: 同一个类里面,同一个方法参数顺序或者个数不同