zoukankan      html  css  js  c++  java
  • 深入浅出C++引用(Reference)类型

    要点1:为反复使用的冗长的变量名称定义一个简短的、易用的别名,从而简化了代码。通常,冗长的变量名称源于多层嵌套对象,例如类中定义嵌套类,类中定义其它类对象。

    //------ 未使用引用的程序片段,反复使用的、冗长的变量名称,极易书写出错 ------

    Computer.Host.Cpu.enBrand = enIntel;

    Computer.Host.Cpu.TypeName = "Core Duo 2 E7400";

    Computer.Host.Cpu.CoreNum = 2;

    Computer.Host.Cpu.FrequencyInGhz = 2.8.;

     

    //------ 使用了引用的程序片段 ---------------------------------------------------------------------

    // 如果编译器支持C++11标准,以简练方式定义引用变量

    auto &rCpu = Computer.Host.Cpu;

    // 如果编译器不支持C++11标准,以标准方式定义引用变量

    TComputer::THost::TCpu &rCpu = Computer.Host.Cpu;

     

    // 使用引用,极大地简化代码,减少了书写出错

    rCpu.enBrand = enIntel;

    rCpu.TypeName = "Core Duo 2 E7400";

    rCpu.CoreNum = 2;

    rCpu.FrequencyInGhz = 2.8.;

     

    要点2const引用类型作为函数参数,拓宽了函数入口参数的接受范围,极大地增加了程序的灵活性和适应性。

    // 声明函数原型如下:

    void ShowMsg(UnicodeString &s);

     

    // 调用函数ShowMsg()入口参数不是UnicodeString类型,编译立即报错

    ShowMsg(10); // 非法10不是UnicodeString类型

    ShowMsg(3.1415926); // 非法3.1415926不是UnicodeString类型

    ShowMsg("This is C-style string.") // 非法"This is C-style string."不是UnicodeString类型

     

    // 将函数原型修改为:

    void ShowMsg(const UnicodeString &s);

     

    // 调用函数ShowMsg()入口参数可以是能够转换为UnicodeString类型的任意数据类型

    ShowMsg (10); // 合法,虽然10不是UnicodeString类型

    ShowMsg (3.1415926); // 合法,虽然3.1415926不是UnicodeString类型

    ShowMsg ("This is C-style string.") // 合法,虽然"This is C-style string."不是UnicodeString类型

     

    const关键字产生了很神奇的效果吧? 想知道为什么吗? 内在的工作机理是:

     

    S1:在函数func()的原型中,如果将入口参数声明为const类型的引用,说明该函数有意修改该入口参数对象的值,入口参数必须是所声明的数据类型,且必须是存在于被调用函数之外的可寻址、可修改(写入)对象。

     

    S2:在函数func()原型中,如果将类型为T的入口参数声明为const类型的引用,说明该函数无意修改该入口参数对象的值,就可以接受能够转换为类型T的任意数据类型,这些数据类型称为可转换类型

     

    QQQ 什么是可转换类型 ???没你想得那么简单!

     

    定义:对于数据类型T,存在一个构造函数,其唯一入口参数是数据类型K,就称K可转换为T。这个构造函数称为可转换构造函数T(K)

     

    QQQ 处理函数调用语句时,编译器如何识别给定的实际参数是可转换类型 ???

    QQQ 如果编译器找到可转换构造函数,又将如何进行编译处理呢 ???

     

    S1:如果函数func()的入口参数要求数据类型const T&, 而程序给定的实际参数的数据类型是K,在处理函数调用语句时,编译器搜索可转换构造函数T(K),若没有找到该函数,编译报错。

    S2:若找到可转换构造函数T(K),编译器就会调用该构造函数生成一个数据类型为T的临时对象,记作t(k),并将t(k)作为实际参数传递给func,形成实际调用func(t(k))

     

    哇哈哈!!!原来函数func()得到的实际参数的数据类型总是为T

    哇哈哈!!!函数func()之所以看起来能够接受多种类型的实际参数,是因为编译器在其中做了许多工作。

     

    QQQ 临时对象t(k)的生命周期是多长 ???

    AAA:在函数func()执行完毕后,临时对象t(k)被自动释放。

     

    重温上面的范例函数:

    // 将函数原型修改为:

    void ShowMsg(const UnicodeString &s);

     

    // 调用函数ShowMsg()入口参数可以是能够转换为UnicodeString类型的任意数据类型

    ShowMsg (10); // 合法int类型→UnicodeString类型

    ShowMsg (3.1415926); // 合法double类型→UnicodeString类型

    ShowMsg ("This is C-style string.") // 合法const char *类型→UnicodeString类型

     

    来看看UnicodeString的构造函数:

    // UnicodeString的构造函数,好大一堆呢,可转换类型真不少 !!!

    __fastcall UnicodeString(): Data(0) {}

    __fastcall UnicodeString(const char* src);

    __fastcall UnicodeString(const UnicodeString& src);

    __fastcall UnicodeString(const wchar_t* src, int len);

    __fastcall UnicodeString(const char* src, int len);

    __fastcall UnicodeString(const wchar_t* src);

    __fastcall UnicodeString(const char16_t* src, int numChar16 = -1);

    __fastcall UnicodeString(const char32_t* src, int numChar32 = -1);

     

    __fastcall UnicodeString(char src): Data(0) { sprintf(L"%c", src);}

    __fastcall UnicodeString(wchar_t src): Data(0) { SetLength(1)[1] = src; }

    __fastcall UnicodeString(short src): Data(0) { sprintf(L"%hd", src); }

    __fastcall UnicodeString(unsigned short src): Data(0) { sprintf(L"%hu", src); }

    __fastcall UnicodeString(int src): Data(0) { sprintf(L"%i", src); }

    __fastcall UnicodeString(unsigned int src): Data(0) { sprintf(L"%u", src); }

    __fastcall UnicodeString(long src): Data(0) { sprintf(L"%ld", src); }

    __fastcall UnicodeString(unsigned long src): Data(0) { sprintf(L"%lu", src); }

    __fastcall UnicodeString(__int64 src): Data(0) { sprintf(L"%Li", src); }

    __fastcall UnicodeString(unsigned __int64 src): Data(0) { sprintf(L"%Lu", src); }

    __fastcall UnicodeString(double src);

    __fastcall UnicodeString(const WideString &src);

    QQQ 是否发现了函数ShowMsg()用到的可转换构造函数 ???

    QQQ 函数ShowMsg()还可以接受哪些数据类型作为入口参数 ???

     

    !!! 千万注意 !!!

    既然将入口参数声明为const T&类型, 函数就不可以修改该参数对象,否则编译报错 !

     

    要点3引用就好像是皇帝的驸马,一次迎娶永远无法解除婚约。

    // 声明或定义一个引用对象时,就好像说"独孤谋是李世民的驸马",因为驸马不可以是单身,不可以不指定公主和皇帝

    // 声明或定义一个引用对象时,必须初始化,指定是哪个变量的别名:

    // 使用 auto关键字声明引用对象,编译器必须支持C++11标准

    auto &rCpu; // 错误,未指定作为哪个变量的别名

    auto &rCpu = Computer.Host.Cpu; // 正确,指定了别名对象

     

    // 声明(定义)之后,无法再次指定作为其它变量的别名,因为驸马是不可以离婚的

    TComputer::THost::TCpu Amd = {enAMD, "A6300", 4, 2.2};

     

    // 此句的意思是把Amd的值赋给rCpu的实际对象Computer.Host.Cpu,而不是作为Amd的别名。

    rCpu = Amd;

    要点4:以引用类型作为函数入口参数:

    S1:参数传递时与指针相同,传递的是对象地址,提高了程序效率;

    S2:在被调用函数内部使用入口参数与使用普通变量完全相同,方便易用

    以引用类型作为函数入口参数

    以指针类型作为函数入口参数

    void ShowComputerInf(TComputer &Computer)

    {

    // 入口参数的用法与使用普通变量完全相同

    ostringstream oss;

    oss << Computer.Brand << endl;

    oss << Computer.TypeId << endl;

    oss << Computer.Price << endl;

    }

    void ShowComputerInf(TComputer *Computer)

    {

    // 入口参数必须按指针方式使用

    ostringstream oss;

    oss << Computer->Brand << endl;

    oss << Computer->TypeId << endl;

    oss << Computer->Price << endl;

    }

    int main(void)

    {

    TComputer Computer;

    // 数据初始化

    // 调用函数,使用对象名称传递对象地址,易用

    ShowComputerInf(Computer);

    return 0;

    }

    int main(void)

    {

    TComputer Computer;

    // 数据初始化

    // 调用函数,传递对象地址必须带"&",易出错

    ShowComputerInf(&Computer);

    return 0;

    }

  • 相关阅读:
    CVE-2020-0796 SMBv3 RCE漏洞检测+复现
    Tomcat文件包含漏洞的搭建与复现:CVE-2020-1938
    Web for pentester_writeup之XML attacks篇
    Web for pentester_writeup之LDAP attacks篇
    PhpStudy2018后门漏洞预警及漏洞复现&检测和执行POC脚本
    2016ACM/ICPC亚洲区沈阳站 E
    CodeForces 599C Day at the Beach(贪心)
    CodeForces 652C Foe Pairs(思维题)
    Codeforces 557D. Vitaly and Cycle(二分图判断)
    poj 1091跳蚤(容斥定理+质因子分解)
  • 原文地址:https://www.cnblogs.com/micemik/p/5014044.html
Copyright © 2011-2022 走看看