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*类型。
.
C++ 构造函数 隐式转换 深度探索,由‘类对象的赋值操作是否有可能调用到构造函数’该实验现象引发的更多相关文章
- c++构造函数隐式转换--转换构造函数
其实我们已经在C/C++中见到过多次标准类型数据间的转换方式了,这种形式用于在程序中将一种指定的数据转换成另一指定的类型,也即是强制转换,比如:int a = int(1.23),其作用是将1.23转 ...
- 了解下Scala隐式转换与柯理化
之前有看过kafka源码,有很多implict声明的方法,当时看的一头雾水,今天趁着空闲,了解下scala 的隐式转换和柯理化相关语法知识. 隐式转换 需要类中的一个方法,但是这个类没有提供这样的一个 ...
- JavaScript学习笔记——数据类型强制转换和隐式转换
javascript数据类型强制转换 一.转换为数值类型 Number(参数) 把任何的类型转换为数值类型 A.如果是布尔值,false为0,true为1 B.如果是数字,转换成为本身.将无意义的后导 ...
- Scala隐式转换和隐式参数
隐式转换 Scala提供的隐式转换和隐式参数功能,是非常有特色的功能.是Java等编程语言所没有的功能.它可以允许你手动指定,将某种类型的对象转换成其他类型的对象或者是给一个类增加方法.通过这些功能, ...
- Scala基础:闭包、柯里化、隐式转换和隐式参数
闭包,和js中的闭包一样,返回值依赖于声明在函数外部的一个或多个变量,那么这个函数就是闭包函数. val i: Int = 20 //函数func的方法体中使用了在func外部定义的变量 那func就 ...
- 大数据技术之_16_Scala学习_06_面向对象编程-高级+隐式转换和隐式值
第八章 面向对象编程-高级8.1 静态属性和静态方法8.1.1 静态属性-提出问题8.1.2 基本介绍8.1.3 伴生对象的快速入门8.1.4 伴生对象的小结8.1.5 最佳实践-使用伴生对象解决小孩 ...
- Scala 学习之路(十三)—— 隐式转换和隐式参数
一.隐式转换 1.1 使用隐式转换 隐式转换指的是以implicit关键字声明带有单个参数的转换函数,它将值从一种类型转换为另一种类型,以便使用之前类型所没有的功能.示例如下: // 普通人 clas ...
- Scala 系列(十三)—— 隐式转换和隐式参数
一.隐式转换 1.1 使用隐式转换 隐式转换指的是以implicit关键字声明带有单个参数的转换函数,它将值从一种类型转换为另一种类型,以便使用之前类型所没有的功能.示例如下: // 普通人 clas ...
- 大数据学习day17------第三阶段-----scala05------1.Akka RPC通信案例改造和部署在多台机器上 2. 柯里化方法 3. 隐式转换 4 scala的泛型
1.Akka RPC通信案例改造和部署在多台机器上 1.1 Akka RPC通信案例的改造(主要是把一些参数不写是) Master package com._51doit.akka.rpc impo ...
随机推荐
- ssm框架之异常处理
异常处理思路 系统中异常包括两类:预期异常和运行时异常runtimeexception,前者通过捕获异常从而获取异常信息,后者主要通过规范代码开发.测试通过手段减少运行时异常的发生. 系统的dao.s ...
- for...in、for...of和forEach
let arr = [1,2,3,4,5,6]; arr.name="AAA"; for(var i in arr){ //遍历的实际是对象的属性名臣,每一个元素的索引被视为一个属 ...
- Vue.use() 不一样的发现
1.Vue.use()首先是可以接受一个函数 var a = function () { console.log(a)}Vue.use(a) // 没有问题会输出a 2.当这个a上面有install属 ...
- unity 真机调试
[Unity3D]Android和ios真机调试测Profiler http://blog.csdn.net/swj524152416/article/details/53466413 Unity5. ...
- openssl1.0在mac下的编译安装(踩坑精华)
之前做了一次brew版本升级,然后用pip3安装的一个python命令就无法执行了(涉及到openssl库),执行就会报一个错误. ImportError: dlopen(/usr/local/Cel ...
- Zabbix housekeeper processes more than 75% busy
原因分析 为了防止数据库持续增大,Zabbix有自动删除历史数据的机制,即housekeeper,而在频繁清理历史数据的时候,MySQL数据库可能出现性能降低的情况,此时就会告警. 一般来说,Zabb ...
- java中变量在内存的位置
package day02; /* * 成员变量:在堆内存中,因为对象的存在,才在内存中存在:作用于整改类中 * 局部变量:在栈内存中:作用于函数中,或者语句中 * */ class car{ //描 ...
- .NET实现可交互的WINDOWS服务(转载自CSDN"烈火蜓蜻")
Windows 服务应用程序在不同于登录用户的交互区域的窗口区域中运行.窗口区域是包含剪贴板.一组全局原子和一组桌面对象的安全对象.由于 Windows 服务的区域不是交互区域,因此 Windows ...
- Ajax跨域解决方案大全
题纲 关于跨域,有N种类型,本文只专注于ajax请求跨域(,ajax跨域只是属于浏览器"同源策略"中的一部分,其它的还有Cookie跨域iframe跨域,LocalStorage跨 ...
- @RequestBody使用说明
@RequestBody 使用 @RequestBody这个对于一般刚接触来说,确实有点陌生,但是现在前端,后端技术分的太细,越来越多的技术层出不穷,前端就分化出POST ,GET,PUT,DELET ...