前言

在类型转换重载函数前加explicit关键字可以限制该函数禁止使用隐式转换[c++11标准]
explicit operator int();
在正常类型直接,比如float x 等于一个 int类型的变量,就会自动发生隐式转换简称隐转,目前就是当类型不同时方便运算。
规则好像就是整数由小转大,整数和浮点数比较转浮点。


正文

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include<iostream>

class Box{
public:
Box(int _val){}
operator int(){
return 1;
}
int operator+(Box b){
return 2;
}
};

int main(){
Box b1{ 100 }, b2{ 200 };
int x = b1 + 100;

return 0;
}

编译器给了报错
愿意在于,我们有两个重载,编译器就不能判断是把b1转成int还是把后面的100转成Box类型。
毕竟两种结果肯定不相同。

然后屏蔽的方法也就是说的关键字explicit。给其中一个做限制。

1
2
3
4
5
6
7
8
9
10
class Box{
public:
Box(int _val){}
explicit operator int(){
return 1;
}
int operator+(Box b){
return 2;
}
};

做了隐转的限制之后,就能成功运行了,它会自动匹配到加法的重载。

注意:explicit只是限制隐式转换,如果还是想要转换就直接强转就行了

比如说:

所以说explicit关键字重点就是限制类的operator重载时发生隐式转换。
至于放在哪个函数都无所谓,比如构造函数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include<iostream>

class Box{
public:
explicit Box(int _val){}
explicit operator int(){
return 1;
}
int operator+(Box b){
return 2;
}
};

int main(){
Box b1{ 100 }, b2{ 200 };
int x = b1 + (Box)100;
std::cout << x;

return 0;
}

因为构造函数无法隐转,所以当下面这种计算的时候,需要手动将100强转成Box类型。


修订hstring之前的问题再继续

1
2
hstring str;
str = str + 123;

operator+重载是实现了,但是+完又赋值给str,就相当于str += 123;
但是cmd能看到输出了23,说明不是完全的错误,可能是内存的问题。
就得回到operator+定义的地方看看。

在此之前,hstring str;
是调用了构造函数

1
2
3
4
5
6
hstring(char ch = 0x32);
hstring::hstring(char ch){
hsmLen = ch; //设置缓冲区
hsLen = 0; //length = 0
c_str = new char[hsmLen];
}

这个是没问题的,因为没有手动传值,他就用默认的设置缓冲区开辟一个。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
hstring &hstring::operator+(int _val){
char str[12]{}; //根据int长度所设
int len = 11; //考虑到符号位
bool flag = _val >= 0; //判断正负数

_val = _val * (flag * 2 - 1); //是否取反
do{
str[--len] = _val % 10 + 48; //int转char
} while (_val /= 10);
str[len = len - 1 * (1 - flag)] = '-' * (flag + 1) * (1 - flag) + str[len] * flag;

unsigned short hslen = hsLen + 12 - len - 1;
hslen += hsLen - 1;
if (hslen > hsmLen){
char *rstr = c_str;
c_str = new char[len]; //重新分配字符串空间
hsmLen = hslen; //重置缓冲区长度
memcpy(c_str, rstr, hsLen);
delete[] c_str; //清理内存
}
memcpy(this->c_str + hsLen - 1, str + len, 12 - len);
hsLen = hslen;

return *this;
}

我们的hsLen一开始如果没有传值,他初始化就是0。
unsigned short hslen = hsLen + 12 - len - 1;
这段倒还好,0+12-len-1。
memcpy(this->c_str + hsLen - 1, str + len, 12 - len);
但是这里就出现问题了,我们的c_str初始化是0x32的长度,hsLen - 1 就是 0 - 1. 说明写入的时候就从c_str[-1]开始写。。。
这样一搞写到前面去了就崩了,不符合逻辑了。

所以要学类那样,即便是空的,也要给他一个字节区分开内存。
这里就是空的初始化时长度要为1。

1
2
3
4
5
hstring::hstring(char ch){
hsmLen = ch; //设置缓冲区
hsLen = 1; //length = 1
c_str = new char[hsmLen];
}

这样就不会有事了。

但是相反,有传值构造的时候,就要先把长度重置了。


然后是另一个加法问题

1
2
3
4
5
hstring str;
str = str + 123;

int x = str + 123;
std::cout << str;

这种情况,str一开始是”123”字符串了,那么当str+123的时候,它是选择将后者隐转,还是前者隐转。
毕竟两个值截然不同,一个“123123” 一个 123+123

当然这里肯定就一种情况,是我们之前写好的operator+

所以还不够,我们要写一个构造函数,让int变成我们的hstring。

1
hstring(int _val);
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
hstring::hstring(int _val):hstring(){
char str[12]{}; //根据int长度所设
int len = 11; //考虑到符号位
bool flag = _val >= 0; //判断正负数

_val = _val * (flag * 2 - 1); //是否取反
do{
str[--len] = _val % 10 + 48; //int转char
} while (_val /= 10);
str[len = len - 1 * (1 - flag)] = '-' * (flag + 1) * (1 - flag) + str[len] * flag;

unsigned short hslen = hsLen + 12 - len - 1;
hslen += hsLen - 1;
if (hslen > hsmLen){
char *rstr = c_str;
c_str = new char[len]; //重新分配字符串空间
hsmLen = hslen; //重置缓冲区长度
memcpy(c_str, rstr, hsLen);
delete[] c_str; //清理内存
}
memcpy(this->c_str + hsLen - 1, str + len, 12 - len);
hsLen = hslen;

//return *this;
}

从数字转char的写法就直接套之前operator的了。

结果反正一样的。

构造函数完成了,就可以去触发之前说的隐转bug了。

不过要先注释掉operator+的方法。

1
2
3
//hstring &operator+(int _val);
//hstring &operator+(unsigned _val);
//.cpp的定义自己注释不做演示。

注释完就能看到报错了,不过跟预期的有出入。

还要对右值进行一个操作,这里就是重载一次

1
2
3
4
5
hstring &operator+(const hstring &&str);

hstring &hstring::operator+(const hstring &&str){
return *this << str;
}

改成这样差不多了。

加右值引用是因为后面的常量内存是临时的,要想办法能够兼容。

就是之前的问题,是前者隐转还是后者隐转。
不需要隐转的时候就在前面加关键字
explicit operator int();

然后就出现了另一个问题。不存在从hstring转换到int的构造。

单独拿出来相加可以,说明是相加之和得到了hstring类型的值。
那么就很简单。。强制转换一下。

然后就然后行得通了。


还有一些其它地方优化听的有点绕了,下次一定


结语

总体上搞了一个半吊子不到的string

运算符重载还是要小心吧,毕竟你只是解决了一种情况,还有很多种情况还需要额外的重载。周而复始看着有点绕。