多态概述
同一个事物表现出多种形态——一词多义
—>
发出同样的消息被不同类型的对象接收时导致完全不同的行为
—>
一个名字 不同的函数
静态多态 :
在编译的过程中确定了同名操作的具体操作对象<函数重载 运算符重载实现>
动态多态 :
在程序运行过程中动态地确定操作所指定的具体对象<虚函数实现>
联编:指一个计算机程序的不同部分彼此关联的过程。按照联编所进行的阶段不同,可分为静态联编和动态联编
vs中程序运行:编译->联编->运行
静态联编 :
联编工作在编译阶段完成的,这种联编过程是在程序运行之前完成的,又称为早期联编。要实现静态联编,在编译阶段就必须确定程序中的操作调用(如函数调用)与执行该操作代码间的关系,确定这种关系称为束定,在编译时的束定称为静态束定。静态联编对函数的选择是基于指向对象的指针或者引用的类型。其优点是效率高,但灵活性差。
(通过以下示例理解)
Code1(理解静态联编):
#include"iostream.h"
class A
{public:
void f(){cout<<"A"<<"";}
};
class B:public A
{public:
void f(){cout<<"B"<<endl;}
};
Void main()
{A *pa=NULL;
A a;
B b;
pa=&a;pa->f();
pa=&b;pa->f();
}
结果:A A
可以看出 通过对象指针进行普通成员函数的调用 仅仅与指针的类型有关
而与此刻正指向什么对象无关。
要想实现当指针指向不同对象时执行不同的操作 就必须将基类中相应的成员函数定义为虚函数
从而实现动态联编
Code2(理解静态联编):
编译时已经确定了同名操作(Area函数)的具体操作对象 对Area函数的选择时基于指向对象的指针或引用的类型
Code3(函数重载):
#include<math.h>
class CMyclass
{ public:
int plus(int, int);
double plus(double,double);
}
int CMyclass::plus(int x,int y)
{}
double CMyclass::plus(double x,double y)
{}
void main()
{
CMyclass Data;
cout<<Data.plus(1,2)<<endl;
cout<<Data.plus(3.3,3.0)<<endl;
}
Code4(运算符重载为成员函数 参数个数=原操作数-1):
将指定的运算表达式转化为对运算符函数的调用(所以是静态多态)
. .* :: ?: sizeof 这五种运算符不能重载
1.双目运算重载
oprd1 B oprd2
重载后相当于oprd1.operator B(oprd2)
2.单目运算重载
U oprd
①重载后相当于 oprd.oprator U() 函数没有形参
例如 前置++重载的格式:
<函数类型> oprator ++()
使用: ++<对象>
②重载后相当于 oprd.oprator++(0)
例如 后置++重载的格式:
<函数类型> oprator ++(int)(为了区别前置运运算)
使用:<对象>++;
但这并没有实现真正意义上的前置++ 后置++
Code5(运算符重载为成员函数 实现前置++ 后置++):
#include<iostream.h>
class CClock
{ int Hour,Minute,Second;
public:
CClock(int NewH=0,int NewM=0,int NewS=0)
{初始化}
void ShowTime()
{输出时分秒}
CClock operator++();
CClock operator++(int);
};
CClock CClock::operator++()
{ Second++:
if(Second>=60)
{Second=Second-60;Minute++;
if(Minute>=60){Minute=Minute-60;Hour++;Hour=Hour%24;}
}
return *this;
}
CClock CClock::operator++(int)
{ CClock old=*this;
++(*this);//利用前置++
return old;
}
void main
{ CClock myClock(23,59,59);
cout<<"First Time";
myClock.ShowTime();
cout<<"Show myclock++";
(myClock++).ShowTime();
cout<<"Show ++myClock";
(++myClock).ShowTime();
思考:如何在没有后置++重载的情况下实现前置++???
}
Code6(运算符重载为友元函数 参数个数=原操作数个数)
=,(),[],->不能重载为友元函数
Code7(运算符重载总结例子)
顺便复习逗号运算 用得少可能都不记得了
#include <iostream>
using namespace std;
class CComplex
{
float real, imag;
public:
CComplex(float xx = 0, float yy = 0) { real = xx; imag = yy; }
~CComplex() { }
CComplex operator+(const CComplex &q);
CComplex operator-(const CComplex &q);
bool operator == (CComplex);
bool operator !=(CComplex);
CComplex operator+=(CComplex);
CComplex operator-=(CComplex);
CComplex operator,(CComplex);
float get_real() { return real; }
float get_imag() { return imag; }
};
//+运算符
CComplex CComplex::operator+(const CComplex &q)
{
return CComplex(real + q.real, imag + q.imag);
}
//-运算符
CComplex CComplex::operator-(const CComplex &q)
{
return CComplex(real - q.real, imag - q.imag);
}
bool CComplex::operator==(CComplex q)
{
if (real == q.real&&imag == q.imag) return 1;
return 0;
}
bool CComplex::operator!=(CComplex q)
{
if (real != q.get_real() || imag != q.get_imag()) return 1;
return 0;
}
CComplex CComplex::operator+=(CComplex q)
{
real += q.real; imag += q.imag;
return *this;
}
CComplex CComplex ::operator-=(CComplex q)
{
real -= q.real; imag -= q.imag;
return *this;
}
CComplex CComplex ::operator,(CComplex q)
{
return CComplex(q.real, q.imag);
}
int main()
{
CComplex k1(1, 2), k2(3, 4), k3(5, 6);
//将k2+k3赋给k1
cout << "(k1,k2+k3):" << (k1, k2 + k3).get_real() << endl;
return 0;
输出
(k1,k2+k3):8
}
关于返回值优化问题:
简单介绍:
返回值优化,是C++的一项编译优化技术.
最大的好处在于:可以省略函数返回过程中复制构造函数的多余调用
1.MyClass M(int a,int b)
{MyClass tmp1(a,b); return tmp1;}
①创建tmp1对象需要构造函数
②然后调用复制构造函数把tmp1拷贝到外部返回值的存储单元里
③最后当tmp1在作用域的结尾时调用析构函数
2.MyClass M2(int a,int b)
{return MyClass(a,b);}
①不调用复制构造函数
②返回临时对象,编译器直接把这个临时对象创建在外部返回值的存储单元,因此仅需要一个普通的构造函数
demo1:
class A
{ public:
A(){...}
A(const A&){...}
~A(){...}
A &operator = (const A&)
{...}
};
A func(){return A();}
void main()
{A a = fun();}
不用调用复制构造函数 func() a可以直接指向fun()返回的临时对象
具体分类:
命名返回值优化(NRVO)
!!如果一个函数的返回值是一个对象!!
那么正常的返回语句的执行过程是,把这个对象[构造函数 ]从当前函数的局部作用域(当前函数的栈空间),拷贝到返回去[复制构造函数 ],使得调用者可以访问。然后程序从当前函数中返回到上一层,即该函数的调用语句处,通过访问返回区的对象,来执行调用语句所在的一整个语句。
优化就是当这个函数中所有的返回语句全部是这一个对象的话,那么,优化就是在这个对象创建的时候,直接在返回区建立,这样使得返回时不需要调用复制构造函数 了
demo1:
未进行优化:(编译后执行)
优化:(编译后执行)
demo2(多层函数中)
未进行优化:
进行了优化:
未命名返回值优化:
!!如果函数返回值不是一个对象!!
这种方法更倾向于一种编程技巧
demo:
运行结果:
思考:
针对MyMehod1:调用构造函数两次 复制构造函数一次(析构函数我先不考虑)
针对MyMehod2:调用构造函数一次(析构函数我先不考虑)
隐式构造函数优化:
demo1:
运行结果:(注意看 没调用复制构造函数)
理解:return右边的A()是直接在函数返回区建立的。而这个返回区,在主函数中,就变成了a所以只需要一个构造函数。
在栈空间中,调用该函数时,会在压入实参之前,留下一个函数返回值类的的大小那么大的空间作为函数的返回区。而新建立的变量a,其地址恰恰就在返回区的这个地方,这两者是完全重合的。所以在函数返回后,无需将函数返回值作为复制构造函数的参数去初始化a。因为a所在的区域,就是函数的返回区
demo2:(多重函数这样返回时)
运行结果和demo1一致