C++学习第五天—多态概述:静态联编、返回值优化问题

本文介绍了C++中的多态概念,包括静态多态(函数重载、运算符重载)和动态多态(虚函数实现)。重点解析了静态联编的工作原理,它在编译阶段完成,效率高但灵活性较差。同时,文章讨论了返回值优化技术,减少复制构造函数的调用,提高程序效率。通过示例代码详细展示了各种情况下的优化效果。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

多态概述

同一个事物表现出多种形态——一词多义
—>
发出同样的消息被不同类型的对象接收时导致完全不同的行为
—>
一个名字 不同的函数
静态多态
编译的过程中确定了同名操作的具体操作对象<函数重载 运算符重载实现>
动态多态
在程序运行过程中动态地确定操作所指定的具体对象<虚函数实现>
联编:指一个计算机程序的不同部分彼此关联的过程。按照联编所进行的阶段不同,可分为静态联编和动态联编

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
}

总结的关于运算符重载知识link

关于返回值优化问题:
简单介绍:
返回值优化,是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一致

返回值优化部分引自

为了在Windows安装ADB工具,你可以按照以下步骤进行操作: 1. 首先,下载ADB工具包并解压缩到你自定义的安装目录。你可以选择将其解压缩到任何你喜欢的位置。 2. 打开运行窗口,可以通过按下Win+R键来快速打开。在运行窗口中输入"sysdm.cpl"并按下回车键。 3. 在系统属性窗口中,选择"高级"选项卡,然后点击"环境变量"按钮。 4. 在环境变量窗口中,选择"系统变量"部分,并找到名为"Path"的变量。点击"编辑"按钮。 5. 在编辑环境变量窗口中,点击"新建"按钮,并将ADB工具的安装路径添加到新建的路径中。确保路径正确无误后,点击"确定"按钮。 6. 返回到桌面,打开命令提示符窗口。你可以通过按下Win+R键,然后输入"cmd"并按下回车键来快速打开命令提示符窗口。 7. 在命令提示符窗口中,输入"adb version"命令来验证ADB工具是否成功安装。如果显示版本信息,则表示安装成功。 这样,你就成功在Windows安装ADB工具。你可以使用ADB工具来执行各种操作,如枚举设备、进入/退出ADB终端、文件传输、运行命令、查看系统日志等。具体的操作方法可以参考ADB工具的官方文档或其他相关教程。\[1\]\[2\]\[3\] #### 引用[.reference_title] - *1* [windows环境安装adb驱动](https://blog.youkuaiyun.com/zx54633089/article/details/128533343)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* *3* [Windows安装使用ADB简单易懂教程](https://blog.youkuaiyun.com/m0_37777700/article/details/129836351)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值