C++坑点集合 - 1 隐式调用和默认实现的构造函数的坑

C++是一个编译器会替你在背后做很多事情的语言,包括模板实例化,按需要创造隐式的构造函数,默认构造你没有显式构造的成员,按需进行隐式转换和饮食构造等等,如果没有彻底了解清楚,就容易被这些编译器背后做好的事情坑到,这个系列文章就来总结我在写C++时遇到的各种坑。

 

所谓隐式调用和默认实现的构造函数,当你写一个赋值语句的时候,编译器会首先检查两个类型又没有直接实现的赋值函数,然后检查赋值左右的类型是否能做隐式转换和构造,转换或者构造好之后,再尝试进行拷贝或移动赋值。这时候,坑点来了,如果你有这么样的一个类:

struct Token
{
    int label;
    string content;
    Token(int _label = -1, string _content = "")
        : label(_label)
        , content(_content)
    {}
};

看它的构造函数,每一个参数都有默认参数,这个东西是会被编译器当成默认构造函数的,并且这个构造函数不会被视为用户自己实现的构造函数,所以编译器依然会按需自动生成其他的[拷贝|移动][构造|赋值]函数,所以这时候坑点来了,以下的语句是合法的:

Token label(1, "hello");

label = 2; //这tm是合法的!!!

label = 2,这条语句会让编译器隐式调用Token的构造函数用2构造一个Token,参数的_content采用默认值“”,然后又调用隐式生成的移动赋值(move assignment)函数,进行赋值。而且毫无警告发生,这样写可能还比较明显,容易发现问题,如果代码复杂起来,被坑的可能性就大大提高了,我自己的tokenizer generator就是有一个bug坑在了这个地方,幸亏IDE能识别出运算符重载以及显示鼠标指向的变量的值,让我很快发现了这个bug,不然又不知道要debug到猴年马月去了。这个问题还说明了使用一个牛逼的IDE的重要性,倘若是用VC6……呵呵呵呵呵。

话说我该把这个构造函数声明为explicit的,忘了

时间: 2024-11-10 19:17:05

C++坑点集合 - 1 隐式调用和默认实现的构造函数的坑的相关文章

equals()和hashCode()隐式调用时的约定

package com.hash; import java.util.HashMap; public class Apple { private String color; public Apple(String color) { this.color = color; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + ((color == nul

android intent隐式调用之一个应用程序启动另一个应用程序(转载)

理解Intent的关键之一是理解清楚Intent的两种基本用法:一种是显式的Intent,即在构造Intent对象时就指定接收者,这种方式与普通的函数调用类似:另一种是隐式的Intent,即Intent的发送者在构造Intent对象时,并不知道也不关心接收者是谁,这种方式与函数调用差别比较大,有利于降低发送者和接收者之间的耦合.另外Intent除了发送外,还可用于广播. 显示调用 1.Intent intent = new Intent(); intent.setClass(A.this,B.c

C++模板之隐式实例化、显示实例化、隐式调用、显示调用和模板特化详解

代码编译运行环境:VS2012+Debug+Win32 模板的实例化指函数模板(类模板)生成模板函数(模板类)的过程.对于函数模板而言,模板实例化之后,会生成一个真正的函数.而类模板经过实例化之后,只是完成了类的定义,模板类的成员函数需要到调用时才会被初始化.模板的实例化分为隐式实例化和显示实例化. 对函数模板的使用而言,分为两种调用方式,一种是显示模板实参调用(显示调用),一种是隐式模板实参调用(隐式调用).对于类模板的使用而言,没有隐式模板实参和显式模板实参使用的说法,因为类模板的使用必须显

spring data redis 遇到的“坑”——set集合的 Srandmember

sRandMember 命令是用于方法 set 集合中的“随机”元素1命令格式为 sRandMember key [count]1以下内容来自 http://www.redis.net.cn/order/3604.html 1.如果 count 为正数,且小于集合基数,那么命令返回一个包含 count 个元素的数组,数组中的元素各不相同. 2.如果 count 大于等于集合基数,那么返回整个集合. 3.如果 count 为负数,那么命令返回一个数组,数组中的元素可能会重复出现多次,而数组的长度为

C++坑点集合 - 2 严格的Multipass Guarantee

之前写了一个char32_iterator,简单说就是封装一个string::const_iterator,在operator*的时候将它引用的utf-8序列转为utf-32编码的单个字符返回--这看上去很简单.平时各位在编程的过程中一定会遇到类似的需求:实现一个惰性的transform,在一个容器的每一个元素上应用一个转换函数,但不是立即应用,而是等到使用它的时候即时转换.这听起来就像C#的Linq或者Java的Stream API.熟悉C++的你,一定会想到封装一个迭代器,就像我前面的cha

基于隐式调用风格的kwic实现

ImplicitlyCallMain类 /**  *   */ package com.jason.kwic.implicitlyCall; import java.io.File; import java.io.IOException; import java.util.List; import java.util.Scanner; /**  * @author jasonzhang  *  */ public class ImplicitlyCallMain { public static 

Swift入坑系列—集合类型

数组(Arrays) 字典(Dictionaries) 数组(Arrays) 在OC里面,NSArray和NSMutableArray这两个类可以存储任意类型的对象,并且不提供所返回对象的任何特别信息.在Swift中,数据值被存入某个数组之前类型必须明确.方法是通过显式的类型标注或者类型推断,而且不是必须是class类型. //定义了一个存储 字符串类型 的可变数组('var'字段修饰) var shoppingList: [String] = ["Eggs", "Milk&

超详细解锁Webpack步骤,跟着我左手右手一个慢动作~~~来吧小伙伴们 一起学起来~~; 如果有坑,可以跟着我跳进去再跳出来哦~~内附填坑操作

webpack 核心 entry: 入口 output: 输出 loader: 模块转换器,用于把模块原内容按照需求转换成新内容 插件(plugins): 扩展插件,在webpack构建流程中的特定时机注入扩展逻辑来改变构建结果或做你想要做的事情 新建一个文件夹 使用 npm init -y 进行初始化(也可以使用 yarn). 会生成一个package.json npm init -y 安装 webpack.webpack-cli:     执行完后会下载包,生成一个node_modules文

Unity3D的IL2CPP平台找不到默认构造函数的坑

最初发现这个问题的情况是,当游戏用IL2CPP平台发布IOS版本的时候,会遇到某些dll格式的插件会导致游戏抛异常崩溃,比如FullInspector和Behavior Designer.所抛的异常是找不到某些类的默认构造函数. 后来发现,不只是某些插件会报这种异常,很多json格式的序列化功能也会在IL2CPP平台上抛找不到默认构造函数的异常. 导致这个问题的原因是,IL2CPP版本在AOT编译时的一些优化机制导致的.想要详细了解这个机制的话可以看文档:http://docs.unity3d.