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*类型。

.

C++ 构造函数 隐式转换 深度探索,由‘类对象的赋值操作是否有可能调用到构造函数’该实验现象引发的更多相关文章

  1. c++构造函数隐式转换--转换构造函数

    其实我们已经在C/C++中见到过多次标准类型数据间的转换方式了,这种形式用于在程序中将一种指定的数据转换成另一指定的类型,也即是强制转换,比如:int a = int(1.23),其作用是将1.23转 ...

  2. 了解下Scala隐式转换与柯理化

    之前有看过kafka源码,有很多implict声明的方法,当时看的一头雾水,今天趁着空闲,了解下scala 的隐式转换和柯理化相关语法知识. 隐式转换 需要类中的一个方法,但是这个类没有提供这样的一个 ...

  3. JavaScript学习笔记——数据类型强制转换和隐式转换

    javascript数据类型强制转换 一.转换为数值类型 Number(参数) 把任何的类型转换为数值类型 A.如果是布尔值,false为0,true为1 B.如果是数字,转换成为本身.将无意义的后导 ...

  4. Scala隐式转换和隐式参数

    隐式转换 Scala提供的隐式转换和隐式参数功能,是非常有特色的功能.是Java等编程语言所没有的功能.它可以允许你手动指定,将某种类型的对象转换成其他类型的对象或者是给一个类增加方法.通过这些功能, ...

  5. Scala基础:闭包、柯里化、隐式转换和隐式参数

    闭包,和js中的闭包一样,返回值依赖于声明在函数外部的一个或多个变量,那么这个函数就是闭包函数. val i: Int = 20 //函数func的方法体中使用了在func外部定义的变量 那func就 ...

  6. 大数据技术之_16_Scala学习_06_面向对象编程-高级+隐式转换和隐式值

    第八章 面向对象编程-高级8.1 静态属性和静态方法8.1.1 静态属性-提出问题8.1.2 基本介绍8.1.3 伴生对象的快速入门8.1.4 伴生对象的小结8.1.5 最佳实践-使用伴生对象解决小孩 ...

  7. Scala 学习之路(十三)—— 隐式转换和隐式参数

    一.隐式转换 1.1 使用隐式转换 隐式转换指的是以implicit关键字声明带有单个参数的转换函数,它将值从一种类型转换为另一种类型,以便使用之前类型所没有的功能.示例如下: // 普通人 clas ...

  8. Scala 系列(十三)—— 隐式转换和隐式参数

    一.隐式转换 1.1 使用隐式转换 隐式转换指的是以implicit关键字声明带有单个参数的转换函数,它将值从一种类型转换为另一种类型,以便使用之前类型所没有的功能.示例如下: // 普通人 clas ...

  9. 大数据学习day17------第三阶段-----scala05------1.Akka RPC通信案例改造和部署在多台机器上 2. 柯里化方法 3. 隐式转换 4 scala的泛型

    1.Akka RPC通信案例改造和部署在多台机器上  1.1 Akka RPC通信案例的改造(主要是把一些参数不写是) Master package com._51doit.akka.rpc impo ...

随机推荐

  1. openCV - 1. 加载、修改、保存图像

    加载 cv::imread 修改 cv::cvtColor 保存 cv::imwrite 加载图像cv::imread imread功能是加载图像文件成为一个Mat对象,其中第一个参数表示图像文件名称 ...

  2. 0 mysql 安装

    1 安装网址 https://dev.mysql.com/downloads/installer/ 选择 mysql server版本一路next 2.配置环境 mysql 默认安装位置是: C:\P ...

  3. 数据库行转列的sql语句

    问题描述 假设有张学生成绩表(CJ)如下Name Subject Result张三 语文 80张三 数学 90张三 物理 85李四 语文 85李四 数学 92李四 物理 82 现在 想写 sql 语句 ...

  4. ZK集群源码解读

    1.1. 集群模式 1.1.1.   数据同步总流程 1.1.1.1. OBSERVING 1.1.1.2. FOLLOWING 1.1.1.3. LEADING 1.1.2. 领导选举 1.1.2. ...

  5. nginx高可用

    15.1. 传统的高可用思路 tomcat的高可用的思路,是在tomcat集群前面加一层负载服务nginx.如下图 这种做法,解决了tomcat的高可用问题.但是引入了前面的负载机器的高可用问题(Ng ...

  6. java实现zip文件的解压

    使用到的包 org.apache.commons 下载文件 url:文件所在地址需要是http:// filePath:将下载的文件保存的路径 public static void getDownlo ...

  7. console线和RJ45

    123 前言 一直以为console口和RJ45是同一种接口,but后来我发现我错了~ RJ45 先介绍一下RJ45吧 什么是RJ45? RJ45是一种接口 我们家用的网线的接口 其实就是RJ45 r ...

  8. CLTPHP 漏洞

    前言 awd小组的第一次训练 0x01 首先看一下主界面 使用的应该是PHP模板,随便翻一下找到一个注册界面 随便注册一个用户,登陆后在设置里找到一个上传点 上传我们的一句话木马 查看返回包,上传成功 ...

  9. PHP复杂变量

    eval($str="${${phpinfo()}}";)     →   可以执行phpinfo() ${phpinfo()} = {${phpindo()}} PHP复杂变量 ...

  10. Spring Boot入门,源码解析

    目录 1.Spring Boot简介 2.微服务 3.Spring Boot HelloWorld 3.1 创建一个Maven工程 3.2 导入依赖Spring Boot相关的依赖 3.3 编写一个主 ...