特殊例子: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*类型。

 

 

.

 

posted @ 2020-08-16 22:42  一匹夫  阅读(199)  评论(0编辑  收藏  举报