1.静态多态:函数重载,运算符重载,复用函数名(编译阶段确定地址)
2.动态多态:子类和虚函数实现运行时的多态(运行阶段确定地址)
- 参数多态(函数模板和类模板)
- 包含多态 (virtual)
- 重载多态(重载多态是指函数名相同,但函数的参数个数或者类型不同的函数构成多态)
- 强制多态(强制类型转换)
其中:
- 类型参数化多态和包含多态统称为一般多态性
- 重载多态和强制多态统称为特殊多态性
- class A
- {
- public:
- void show(){cout << "父类show()" << endl;}
- };
- class B :public A
- {
- public:
- void show(){cout << "子类show()" << endl;}
- };
- //父类引用可以接受子类对象
- void show1(A &a)
- {
- a.show();
- }
- //父类指针接收子类对象
- void show2(A*p)
- {
- p->show();
- }
-
- int main()
- {
- B b;
- //结果输出 父类的show 因为编译阶段 show1函数提早绑定了父类地址
- show1(b);
- B *b1 = new B;
- //结果输出 父类的show 因为编译阶段 show1函数提早绑定了父类地址
- show2(b1);
- return 0;
- }

在这里我们发现,我声明的时一个子类对象,但有父类指针或引用去接收时,调用的函数却是父类的,为了解决这个问题,c++提供了虚函数的概念。
虚函数 是在基类中使用关键字 virtual 声明的函数,在派生类中重写这个函数,当父类指针(引用)接收子类对象时,会采用动态多态,在运行阶段确定地址,会使用子类的函数子类重写父类虚函数时:子类的virtual可加可不加
- class A
- {
- public:
- virtual void show(){cout << "父类show()" << endl;}//声明为虚函数
- };
- class B :public A
- {
- public:
- void show(){cout << "子类show()" << endl;}
- };
- //父类引用接受子类对象
- void show1(A &a)
- {
- a.show();
- }
- //父类指针接收子类对象
- void show2(A*p)
- {
- p->show();
- }
-
- int main()
- {
- B b;
- //结果输出 子类的show 因为运行阶段 show1函数最后绑定了父类地址
- show1(b);
- B *b1 = new B;
- //结果输出 子类的show 因为运行阶段 show1函数最后绑定了父类地址
- show2(b1);
- return 0;
- }

C++提供多态的目的是:可以通过基类指针对所有派生类(包括直接派生和间接派生)的成员变量和成员函数进行“全方位”的访问,尤其是成员函数。如果没有多态,我们只能访问成员变量。
格式:
virtual 函数类型 函数名(参数)=0;
声明为纯虚函数,则该函数可以不用实现 (也可以实现),拥有纯虚函数的类也称为 抽象类
1.抽象类不能实例化,但可以定义一个抽象类 指针和引用
2.子类一般需要重写抽象类的纯虚函数,不然也成抽象类
- class person
- {
- public:
- //纯虚函数
- virtual void show() = 0;
-
- int age;
- };
- class son:public person
- {
- public:
- //重写父类的纯虚函数 子类未重写的话也为抽象类
- void show()
- {
- age = 20;
- cout << "son中age的值" << this->age << endl;
- }
- };
-
- int main()
- {
- //纯虚函数不能实例化
- //person p;//报错
- //但可以使用指针和引用
- //vector 为容器,类似数组
- vector<person*>p;
- vector<person&>p1;
- //非纯虚函数可以实例化
- son s;
- return 0;
- }
什么样的函数不能声明为虚函数?
- 不能被继承的函数。
- 不能被重写的函数。
- 普通函数,友元函数,构造函数,内联成员函数,静态成员函数
1)普通函数
普通函数不属于成员函数,是不能被继承的。普通函数只能被重载,不能被重写,因此声明为虚函数没有意义。因为编译器会在编译时绑定函数。
而多态体现在运行时绑定。通常通过基类指针指向子类对象实现多态。
2)友元函数
友元函数不属于类的成员函数,不能被继承。对于没有继承特性的函数没有虚函数的说法。
3)构造函数
首先说下什么是构造函数,构造函数是用来初始化对象的。假如子类可以继承基类构造函数,那么子类对象的构造将使用基类的构造函数,而基类构造函数并不知道子类的有什么成员,显然是不符合语义的。从另外一个角度来讲,多态是通过基类指针指向子类对象来实现多态的,在对象构造之前并没有对象产生,因此无法使用多态特性,这是矛盾的。因此构造函数不允许继承。
4)内联成员函数
我们需要知道内联函数就是为了在代码中直接展开,减少函数调用花费的代价。也就是说内联函数是在编译时展开的。而虚函数是为了实现多态,是在运行时绑定的。因此显然内联函数和多态的特性相违背。
5)静态成员函数
首先静态成员函数理论是可继承的。但是静态成员函数是编译时确定的,无法动态绑定,不支持多态,因此不能被重写,也就不能被声明为虚函数。
- 虚析构是把析构函数设置为虚函数,纯虚析构的话,是把析构函数弄成纯虚函数
- 虚析构和纯虚析构都需要实现
- 纯虚析构在类内声明,类外实现
当子类开辟到堆区时,父类指针释放时无法调用子类析构代码虚析构的作用:当父类指针(或引用)接收子类在堆区生成的对象时,父类可以调用子类的析构函数
当父类析构不是虚析构时:(delete 父类指针时不会调用子类的析构函数)
- class person
- {
- public:
- person()
- {
- cout << "person的构造函数" << endl;
- }
- //纯虚函数
- virtual void show() = 0;
- //析构
- ~person()
- {
- cout << "person的析构函数" << endl;
- }
-
- };
- class son :public person
- {
- public:
- son(int a)
- {
- age = new int(a);
- cout << "son的构造函数" << endl;
- }
- //重写父类的纯虚函数 子类未重写的话也为抽象类
- void show()
- {
- cout << "son中age的值" << this->age << endl;
- }
- ~son()
- {
- if (age != NULL)
- {
- delete age;
- age = NULL;
- }
- cout << "son的析构函数" << endl;
- }
- int* age;
- };

把父类析构函数设置为虚析构时:
- //虚析构
- virtual ~person()
- {
- cout << "person的析构函数" << endl;
- }

把父类析构函数设置为纯虚析构时:
- //纯虚析构 类内声明
- virtual ~person() = 0;
-
-
- //类外实现
- person::~person()
- {
- cout << "person的纯虚析构函数" << endl;
- }

正在上传…重新上传取消正在上传…重新上传取消正在上传…重新上传取消正在上传…重新上传取消
- 虚函数可以为private,并且可以被子类覆盖(因为虚函数表的传递),但子类不能调用父类的private虚函数。虚函数的重载性和它声明的权限无关。
- virtual修饰符则强调父类的成员函数可以在子类中被重写,因为重写之时并没有与父类发生任何的调用关系,故而重写是被允许的。
- 纯虚函数可以设计成私有的,不过这样不允许在本类之外的非友元函数中直接调用它,子类中只有覆盖这种纯虚函数的义务,却没有调用它的权利。
- class person
- {
- public:
- private:
- virtual void show()
- {
- cout << "父类的show()函数" << endl;
- }
- };
-
- class son :public person
- {
- public:
- void show()
- {
- cout << "子类的show()函数" << endl;
- }
- };
-
- int main()
- {
- son s;
- s.person::show();//子类无法访问父类的私有成员
- return 0;
- }
c++规定: = [ ] () -> 这四个运算符只能被重载为类的非静态成员函数重载
其他的可以被友元重载
解释:
其他运算符重载函数都会更具参数类型或数目进行精确匹配,
这4个不具有这总检查功能,使用友元定义就会出错
sizeof : ? . ::
长度运算符 三目运算符 成员选择符 域解析符
运算符重载规则:
- 只能重载C++已有的运算符
- 重载的功能应该与原有功能相似(一般不要 重载 + 号用 - 号的作用)
- 重载子后,优先级和结合性不变
- person operator+(const person &p)
- {
- this->age += p.age;
- this->height += p.height;
- return *this;
- }
-
- int age;
- int height;
全局函数:
- person operator+(const person &p,const person &p1)
- {
- person p2;
- p2.age = p.age + p1.age;
- p2.height = p.height + p1.height;
- return p2;
- }
想要实现连续运算:可以返回类的引用即可实现
全局函数:
- person& operator+(const person &p,const person &p1)
- {
- person p2;
- p2.age = p.age + p1.age;
- p2.height = p.height + p1.height;
- return p2;
- }
前置++:++x
-
- person& operator++()
- {
- age++;
- height++;
- return *this;
- }
-
- int age;
- int height;
后置++:x++
- //int为占位参数用来区分 前置++和后置++
- person& operator++(int)
- {
- person p = *this;
- age++;
- height++;
- return p;
- }
一般用全局函数实现,成员函数难以实现
cout 的类为 ostream 输出流类
cin 的类为 istream 输出流类
- class person
- {
- public:
- //有参构造
- person(int a,string b):age(a),name(b){}
-
- int age;
- string name;
- };
- //全局函数重载<<
- ostream& operator<<(ostream& o,const person &p1)
- {
- cout << "年龄为:" << p1.age << "姓名为:" << p1.name << endl;
- return cout;
- }
- int main()
- {
- person p(10, "a");
- //直接输出 age和name
- cout << p<<endl;
- return 0;
- }
重载的格式:
- 返回值为 void*
- 参数为size_t size,表示开辟空间的大小
重载new:(成员函数)
- void * operator new(size_t size)
- {
- void* p = malloc(size);
- }
重载delete:(成员函数)
- void operator delete(void *)
- {
- free(p);
- }
- 一种为仿函数
- 直接重载
- class person
- {
- public:
- //仿函数:用法与函数类似
- void operator()()
- {
- cout << "仿函数" << endl;
- }
- int operator()(int a, int b)
- {
- return a + b;
- }
- };
-
- int main()
- {
- person p;
- //输出一段话
- p();
- //输出两个数的和
- p(10, 20);
- return 0;
- }