zoukankan      html  css  js  c++  java
  • 特殊例子:C++ 构造函数 隐式转换 深度探索,由‘类对象的赋值操作是否有可能调用到构造函数’该实验现象引发

    Test1 /** Ques: 类对象的赋值操作是否有可能调用到构造函数 ? **/

    class mystring {
        char str[100];
    public:
        mystring() //mystring(): str{0x37} 使用初始化列表,实际只初始化了数组中的第一个元素
        {
                 //cout << str[0] <<  << str[55] << endl;
            memset(str, 0, sizeof(str));
            cout << "mystring()" << endl;
        }
        mystring(char* s_ptr) : str{ 0 }
        {
            cout << "mystring(char* s_ptr)" << endl;
            memcpy(str, s_ptr, strlen(s_ptr));
        }
        mystring& operator+ (mystring& other)
        {
            cout << " mystring& operator+ (mystring& other)" << endl;
            memcpy(str, other.str, strlen(other.str));
        }
        mystring& operator+ (mystring other)
        {
            cout << " mystring& operator+ (mystring other)" << endl;
            memcpy(str, other.str, strlen(other.str));
        }
        void print()
        {
            cout << str << endl;
        }
    };
    
    int main()
    {
        char  b[10]= "12345";
    
        mystring mystr;
        mystr = "hello"; // 这句代码感觉有玄机,不对劲。 (最终的解释是这里进行了一次隐式转换,转到了char*,匹配上了char*的构造函数,产生了一个mystring类的临时对象,然后赋值给了mystr)
        mystr.print();
    
        mystr = b;
        mystr.print();
    
        return 0;
    

    实测 , 貌似会。
    这种情况发生在重载当中。
    在进行类对象赋值时,在现有情况下没有更合适的重载函数版本的时候,一眼看去像是:构造函数也会作为重载函数版本来考虑,以尽量保证编译通过。
    但是构造毕竟是构造,这里明显是赋值, 构造和赋值是两个时间段,一前一后的事情,赋值的时候肯定事先已经构造完了。 分析问题时,要记住并遵循这个基本原则。
    那么这到底是怎么回事呀? 先看下一个demo。

    /**  Test2
    ** 新增一个重载赋值操作符=后, 本实验的结果就会优先调用重载赋值操作符=的函数版本,而不是之前的现象。
    **/

    class mystring {
        char str[100];
    public:
        mystring() 
        {
            memset(str, 0, sizeof(str));
            cout << "mystring()" << endl;
        }
        mystring(char* s_ptr) 
        {
            cout << "mystring(char* s_ptr)" << endl;
            memset(str, 0, sizeof(str));
            memcpy(str, s_ptr, strlen(s_ptr));
        }
    
        mystring& operator=(char* s_ptr)
        {
            cout << "mystring& operator=(char* s_ptr)" << endl;
            memcpy(this->str, s_ptr, strlen(s_ptr));
            return *this;
        }
    
        mystring& operator+ (mystring& other)
        {
            cout << " mystring& operator+ (mystring& other)" << endl;
            memcpy(str, other.str, strlen(other.str));
        }
        mystring& operator+ (mystring other)
        {
            cout << " mystring& operator+ (mystring other)" << endl;
            memcpy(str, other.str, strlen(other.str));
        }
        void print()
        {
            cout << str << endl;
        }
    };
    
    int main()
    {
        mystring mystr;
        mystr = "hello"; 
        mystr.print();
    
        return 0;
    }
    

    再看下一个demo

     Test3

    class mystring {
        char str[100];
    public:
        mystring()
        {
            memset(str, 0, sizeof(str));
            cout << "mystring()" << endl;
        }
        // 新增参数为mystring类的重载赋值符号=的函数版本 ,使用引用
        mystring& operator=(mystring& s_ptr) 
        {
            cout << "mystring& operator=(mystring& s_ptr)" << endl;
            memcpy(this->str, s_ptr.str, strlen(s_ptr.str));
            return *this;
        }
        // 新增参数为mystring类的重载赋值符号=的函数版本 ,使用值传递
        mystring& operator=(mystring s_ptr)
        {
            cout << "mystring& operator=(mystring s_ptr)" << endl;
            memcpy(this->str, s_ptr.str, strlen(s_ptr.str));
            return *this;
        }
    
        #if 0 // 屏蔽这个重载赋值符号=的函数版本
            mystring& operator=(char* s_ptr)
            {
                cout << "mystring& operator=(char* s_ptr)" << endl;
                memcpy(this->str, s_ptr, strlen(s_ptr));
                return *this;
            }
        #endif
    
        mystring& operator+ (mystring& other)
        {
            cout << " mystring& operator+ (mystring& other)" << endl;
            memcpy(str, other.str, strlen(other.str));
        }
        mystring& operator+ (mystring other)
        {
            cout << " mystring& operator+ (mystring other)" << endl;
            memcpy(str, other.str, strlen(other.str));
        }
        void print()
        {
            cout << str << endl;
        }
    };
    
    int main() 
    {
        mystring mystr;
        mystr = "hello";//编译报错,找不到对应的重载版本 ?
        mystr.print();
    
        return 0;
    }
    


    为什么我自己实现的赋值操作符重载函数版本,编译器就不让从char*隐式转换到mystring的临时对象呢?
    编译器默认提供的赋值操作符重载函数版本就可以从char*隐式到string类临时对象 ?

    答案,可能的解释,先这样理解吧:
    "hello"是字符串字面量,
    字符串字面量,进行函数重载时的类型匹配不好描述,
    但是一般要提供operator=( char* )。
    这里到operator=( char* ),已经使用了一次隐式转换的就,没有第二次隐式转换的机会了。


    “hello”这个类型进行函数重载匹配时可能不是char*,而是char[6]。
    如果从char[6]转换为char*,再转换为mystring,就需要进行2次 类型转换,
    而隐式类型转换最多只允许进行一次。

    这里连续三个实验,对于最上面的实验,现在的解释是:
    operator=的参数使用隐式转换的时候,导致构造函数的调用

    重要的事情说三遍:
    字符串字面量,进行函数重载时的类型匹配不好描述,但是一般要提供operator=( char* )
    字符串字面量,进行函数重载时的类型匹配不好描述,但是一般要提供operator=( char* )
    字符串字面量,进行函数重载时的类型匹配不好描述,但是一般要提供operator=( char* )

     

    C++中的explicit关键字只能用于修饰只有一个参数的类构造函数,用于杜绝隐式转换的发生。

    学习了explicit关键字后,

    关于这里是否出现了隐式转换,可以使用explicit关键字,增加一个补充实验:

    在test1的mystring(char* s_ptr)成员函数前加上explicit关键字修饰。

     之后进行编译:

    根据此时的编译信息可知:字符串字面量“hello”的类型是 const char[6], b的类型为char[10]  . 都不是重载函数内的char*类型。

    .

    /************* 社会的有色眼光是:博士生、研究生、本科生、车间工人; 重点大学高材生、普通院校、二流院校、野鸡大学; 年薪百万、五十万、五万; 这些都只是帽子,可以失败千百次,但我和社会都觉得,人只要成功一次,就能换一顶帽子,只是社会看不见你之前的失败的帽子。 当然,换帽子决不是最终目的,走好自己的路就行。 杭州.大话西游 *******/
  • 相关阅读:
    Excelpackage的简单使用(导出用户信息并保存)
    set nocount on/off的作用,可配合存储过程使用
    在sql中case子句的两种形式
    C#开发微信门户及应用(1)--开始使用微信接口(转)
    张建总的一封信
    Jquery在线工具
    《EnterLib PIAB深入剖析》系列博文汇总 (转)
    微软企业库5.0 学习之路——第十步、使用Unity解耦你的系统—PART1——为什么要使用Unity? (转)
    结合领域驱动设计的SOA分布式软件架构 (转)
    ENode框架旨在帮助我们开发ddd,cqrs、eda和事件采购风格的应用程序。(netfocus 汤雪华)
  • 原文地址:https://www.cnblogs.com/happybirthdaytoyou/p/13514862.html
Copyright © 2011-2022 走看看