一 、多态性
1.多态性概述:多态是指同样的消息被不同类型的对象接受时导致不同的行为
2.多态实现:编译时的多态:在编译的过程中确定了同名操作的具体对象。
运行时的多态:在程序运行过程中动态地确定操作所针对地具体现象。
这种确定操作的具体对象的过程就是绑定——指计算机程序自身彼此关联的过程。
绑定工作在编译连接阶段完成的情况称为静态绑定;在运行阶段完成的情况称为动态绑定。
3.运算符重载
1)运算符重载概念:对已有的运算符赋予多重含义,是同一个运算符作用于不同类型的数据时导致不同的行为。
2)运算符重载规则:a)C++中的运算符除了少数几个外,全部都可以重载,而且只能重载C++中已经有的运算符
b)重载后运算符的优先级和结合性都不会改变
c)重载功能应与原有功能相类似,不能改变原运算符的操作对象个数,同时至少有一个操作对象是自定义类型
3)重载形式
a)类的非静态成员函数:
返回类型 operator 运算符(形参表){ 函数体 }
b)重载为非成员函数:
返回类型 operator 运算符(形参表){ 函数体 } 【ps:二者在声明时不同】
注意:当运算符重载为类的成员函数时,函数的参数个数比原来的操作数个数要少一个(后置“++”,“--”除外);
当重载为非成员函数时,参数个数与原操作数个数相同
----------------------------------------------------------------------------------------------
a)类的非静态成员函数
oprd1 B oprd2 ,其中oprd1 为A类对象,则应把B重载为A类的成员函数,该函数只有一个形参,形参类型是oprd2 的所属类型;
oprd++ 或 oprd--,其中oprd 为A类的对象,那么运算符就应该重载为A类的成员函数,这时函数要带一个整形(int)形参——用于辨析前置还是后置
用代码实现:
1 #include<iostream> 2 using namespace std; 3 4 class A { 5 private: 6 int x; 7 public: 8 A(int x = 0) :x(x) {} 9 A operator+(A &x); //重载+ 10 A& operator++(); //前置++ 11 A operator++(int); //后置++ 12 friend ostream & operator<<(ostream &out, const A &a); //重载<< 13 void show() { cout << x << endl; } 14 }; 15 A A::operator+(A &x) { 16 this->x += x.x; 17 return *this; 18 } 19 A& A::operator++() { //前置++ 20 this->x += 1; 21 return *this; 22 } 23 A A::operator++(int) { //后置++ 24 A old = *this; 25 ++(*this); //调用前置++函数 26 return old; 27 } 28 ostream &operator<<(ostream &out, const A &a) { 29 out << a.x << endl; 30 return out; 31 } 32 int main() { 33 A a(10), b(20); 34 cout << "a = " << a << endl << "b = " << b << endl; 35 cout <<"a+b="<< a + b << endl; 36 cout <<"a++ = "<< a++ << endl; 37 cout << "after a++ ,a = " << a << endl; 38 return 0; 39 }
执行结果:
b)重载为非成员函数
对于双目运算符B,实现oprd1 B oprd2 ,其中oprd1和oprd2中只要有一个具有自定义类型,则可以把B重载为非成员函数,该函数形参为oprd1 和 oprd2;
oprd++ 或 oprd--,其中oprd 为自定义类型,那么运算符就可以重载为非成员函数,这时形参1为oprd 另有一个形参2区别前置 后置(int)
我们用上述成员函数的例子来代码实现:
1 #include<iostream> 2 using namespace std; 3 4 class A { 5 private: 6 int x; 7 public: 8 A(int x = 0) :x(x) {} 9 friend A operator+(A a,A b); 10 friend A& operator++(A &a); 11 friend A operator++(A &a, int); 12 friend ostream & operator<<(ostream &out, const A &a); //重载<< 13 void show() { cout << x << endl; } 14 }; 15 A operator+(A a, A b) { //非成员函数的重载+ 16 A x; 17 x.x = a.x + b.x; 18 return x; 19 } 20 A& operator++(A &a) { //非成员函数的前置++ 21 a.x += 1; 22 return a; 23 } 24 A operator++(A &a, int) { //非成员函数的后置++ 25 A old = a; 26 ++a; 27 return old; 28 } 29 30 ostream &operator<<(ostream &out, const A &a) { 31 out << a.x << endl; 32 return out; 33 } 34 int main() { 35 A a(9), b(19); 36 cout << "a = " << a << endl << "b = " << b << endl; 37 cout <<"a+b="<< a + b << endl; 38 cout <<"a++ = "<< a++ << endl; 39 cout << "after a++ ,a = " << a << endl; 40 return 0; 41 }
执行结果:
特别地:
“<<” 运算符的重载
ostream & operator << (ostream &out,const 类型说明符 &对象) 【注意着色的几个地方 是必要的】
4、虚函数
1)一般虚函数成员的声明语法是: virtual 函数类型 函数名 (形参表);
虚函数一般不声明为内联函数,因为对虚函数的调用需要动态绑定
我们用代码实现,理解虚函数的用处
代码如下:
1 #include<iostream> 2 using namespace std; 3 4 class Father { 5 private: 6 int x; 7 public: 8 Father(int x = 1) :x(x) {} 9 virtual void show() { cout << "x of Father is " << x << endl; } 10 }; 11 class Son :public Father{ 12 private: 13 int x; 14 public: 15 Son(int x = 10) :x(x) {} 16 void show() { cout << "x of Son is " << x << endl; } 17 }; 18 int main() { 19 Father f; 20 f.show(); 21 Son s; 22 s.show(); 23 24 return 0; 25 }
执行结果:
可能这样没有什么说服力,因为上述代码中Father 的成员函数show()不加virtual 也可以实现上面这个结果
那我们接着看看下面两个代码的对比
1 #include<iostream> 2 using namespace std; 3 4 class Father { 5 private: 6 int x; 7 public: 8 Father(int x = 1) :x(x) {} 9 void show() { cout << "x of Father is " << x << endl; } //不加virtual 10 }; 11 class Son :public Father{ 12 private: 13 int x; 14 public: 15 Son(int x = 10) :x(x) {} 16 void show() { cout << "x of Son is " << x << endl; } 17 }; 18 int main() { 19 Father f; 20 f.show(); 21 Son s; 22 s.show(); 23 24 Father* fs = &s; 25 fs->show(); 26 return 0; 27 } 28
主要看加粗代码,执行结果:
接着:
1 #include<iostream> 2 using namespace std; 3 4 class Father { 5 private: 6 int x; 7 public: 8 Father(int x = 1) :x(x) {} 9 virtual void show() { cout << "x of Father is " << x << endl; } //加virtual 10 }; 11 class Son :public Father{ 12 private: 13 int x; 14 public: 15 Son(int x = 10) :x(x) {} 16 void show() { cout << "x of Son is " << x << endl; } 17 }; 18 int main() { 19 Father f; 20 f.show(); 21 Son s; 22 s.show(); 23 24 Father* fs = &s; 25 fs->show(); 26 return 0; 27 } 28
依旧看加粗代码,执行结果:
这时我们发现 用基类指针指向派生类的对象时,出现了不一样的地方,使用了virtual 后派生类与基类的同名函数 在派生类中的那个函数彻底覆盖了基类原有的函数。
使用虚函数可以将一个基类函数在继承后,将这个基类函数的执行方式交给派生类掌控,这也是多态性的体现。
当然可以将基类声明为一个抽象类——即内容具有极高的可扩展性,却可以统一一些派生类所共同需要的函数(避免不同派生类取不同名的函数,却做相同的工作 or 基类指针指向它们的对象时,不能调用它们自己的函数)
例如
#include<iostream> class Shape { public: virtual double getArea()const = 0; }; class Circle :public Shape { private: double r = 1; public: double getArea()const { return r * r*3.14; } }; class Rectangle :public Shape { private: double l = 1, w = 2; public: double getArea()const { return w; } }; void showArea(Shape*ptr) { std::cout << ptr->getArea() << std::endl; } int main() { Circle c; Rectangle r; showArea(&c); showArea(&r); return 0; }
执行结果:
正确得到结果
这里我们还是用了纯虚函数即在虚函数后加“=0”可以彻底把这一函数交给派生类去掌控,纯虚函数在基类中可以不实现。
还有在重写继承来的虚函数时,如果函数有默认的形参值,不要重新定义不同的值,举个‘栗子’
代码:
#include<iostream> using namespace std; class A { public: virtual void show(int x = 1) { cout << "x = " << x << endl; } }; class B :public A { public: void show(int x = 2) { //更改形参,原本应该 x = 1 cout <<"x = "<< xx << endl; } }; int main() { B b; A *ptr = &b; ptr->show(); return 0; }
更改前:
输出 x = 1;
更改后:
输出 x = 1;
这是因为,虚函数是动态绑定的,但默认形参是静态绑定的,也就是说通过一个指向派生类的基类指针,可以访问到派生类的虚函数,但是默认形参却只能来自基类定义
-------------------------------------------
为什么使用虚函数?
如果派生类需要修改基类的行为(即重写与基类函数同名的函数)就应该在基类中将相应的函数声明为虚函数;
相反的,在基类中不希望被派生类更改的就应该声明为非虚函数。
补充知识:
dynamic_cast 的使用
目的:将执行基类向派生类转换
理解:基类指针本来只能指向基类的成员,哪怕是指向派生类对象,这个指针仍旧只能指向基类的成员(先除去virtual),
现在用dynamic_cast可以转换基类指针
使用条件:1.积累的指针或引用确实绑定到派生类的对象上;2.只有当基类至少含有一个虚函数即基类是多态类
代码实现:
#include<iostream> class base{ public: int x = 99; virtual ~base() {} }; class derived :public base { public: int y = 21; ~derived() {} }; int main() { base *ptr = new derived; if (dynamic_cast<derived*>(ptr) != NULL) { //因为如果转换失败会返回NULL std::cout << dynamic_cast<derived*>(ptr)->y << std::endl; } if (typeid(*ptr) == typeid(derived)) { std::cout << "1" << std::endl; } return 0; }
===============================
以上为现阶段的学习,如果有误望指正:)