用了很久的C++ 了,今天突然被小伙伴问道,构造函数前的explicit 关键字什么作用,于是自己也只能支支吾吾的说出“为了显式调用……”很尴尬啊……典型的知道所以然不知道其所以然……好吧,搜搜资料好好地充充电……
首先说定义,C++ 中的explicit关键字只能用于修饰只有一个参数的类构造函数,它的作用是表明该构造函数是显式的,而非隐式的。跟他对应的另一个关键字是implicit,意思是隐藏的,类构造函数默认情况下就是声明为implicit(隐式的)。
这里的重点就是 只能 修饰 只有一个参数的 类的构造函数。也就是说,
一、只可以修饰类的构造函数,一般的函数不能修饰。
二、只有类的构造函数有一个参数时候,才有效,当有多个参数时候就没什么意义了(这里有个例外,就是当除了第一个参数以外的其他参数都有默认值的时候,explicit依然有效,此时调用构造函数时只传入一个参数,等效于只有一个参数的类构造函数)。
我们先来看看什么是显式调用,什么是隐式调用:
class MyClass //没有使用explicit关键词,默认为隐式声明
{
public:
int m_size;
char *m_pStr;
MyClass(int size) //没有使用explicit关键词,默认为隐式声明
{
m_size = size; //大小
m_pStr = (char *)malloc(size + 1); //给m_pstr 分配size的大小的内存
memset(m_pStr, 0, size + 1);
}
MyClass(const char *pStr) //没有使用explicit关键词,默认为隐式声明
{
m_size = strlen(pStr);
m_pStr = (char *)malloc(m_size + 1); //给m_pstr分配内存
strcpy(m_pStr, pStr); //复制内容
}
MyClass(int size, const char *pStr) //参数为两个,所以不管是否使用explicit关键字,都是无效的。
{
m_size = size;
int size1 = strlen(pStr); //大小
m_pStr = (char *)malloc(size1 + 1); //给m_pstr分配内存
strcpy(m_pStr, pStr);
}
};
下面是调用:
MyClass c1(10); // ok, 显式调用 MyClass(int size)
MyClass c2 = 12; // ok, 隐式调用 MyClass(int size)
MyClass c3; // 错误, 没有响应的构造函数
MyClass c4("hello"); // ok, 显式调用 MyClass(const char *pStr)
MyClass c5 = "nihao"; // ok, 隐式调用
MyClass c6(5, "hello"); // ok, 显式调用 MyClass(int size, const char *pStr)
c1 = 5; // ok, 隐式调用 MyClass(int size)
c2 = 6; // ok, 隐式调用 MyClass(int size)
c4 = "hello world"; // ok, 隐式调用 MyClass(const char *pStr)
c3 = c1; // 错误, 编译是没问题的,但是必须在类里重载“=”运算符才行
上面代码 c2 = 12; c5 = "nihao"为什么是可以的呢?
因为在C++中,如果构造函只有一个参数时,那么编译的时候就会有一个缺省的转换操作:将该构造函数对应的数据类型的数据转换为该类对象。
也就是说MyClass c2 = 12;等同于MyClass c2(12);
但是上面的代码中的MyClass c2(12),中12表示分配内存大小,那么c2 = 12 与5 = "nihao"这样的隐式转换显得不伦不类,容易被误解,怎么避免使用这样的隐式转换呢?答案就是使用关键字explicit,我们将类中的构造函数添加上explicit关键字试试:
class MyClass
{
public:
int m_size;
char *m_pStr;
explicit MyClass(int size)
{
m_size = size; //大小
m_pStr = (char *)malloc(size + 1); //给m_pstr 分配size的大小的内存
memset(m_pStr, 0, size + 1);
}
explicit MyClass(const char *pStr)
{
m_size = strlen(pStr);
m_pStr = (char *)malloc(m_size + 1); //给m_pstr分配内存
strcpy(m_pStr, pStr); //复制内容
}
explicit MyClass(int size, const char *pStr) //两个参数,所以添加explicit是无效的。
{
m_size = size;
int size1 = strlen(pStr); //大小
m_pStr = (char *)malloc(size1 + 1); //给m_pstr分配内存
strcpy(m_pStr, pStr);
}
};
再次编译一下结果如下:
发现隐式调用的全部错误,这就是使用关键字explicit,显式调用的效果。也就是说,关键字explicit的作用就是防止够咱函数的隐式转换。
上面也说过了,explicit关键字只对有一个参数的类构造函数有效,如果函数的参数大于或者等于两个时候,是不会产生隐式转换的,所以explicit也就是无效的。一个例外就是,当除了第一个参数意外其他参数都有默认值的时候,explicit关键字依然有效,此时当调用构造函数时,只传入一个参数,等同于只有一个参数的类构造函数。
explicit MyClass(int size, const char *pStr = "hello")
{
m_size = size;
int size1 = strlen(pStr); //大小
m_pStr = (char *)malloc(size1 + 1); //给m_pstr分配内存
strcpy(m_pStr, pStr);
}
上面这种情况,explicit也是有效的!