上文代码中几点问题的解决和思考(解决[字符串类型]造成的二异性问题“ note: candidate: ‘operator[](long int, const char*)’ (built-in)“)

文章讲述了在C++编程中遇到的关于二异性问题的解决过程,涉及到字符串常量解析歧义,以及如何正确使用sizeof和strlen计算字符串长度以避免内存分配错误。作者通过实例和解决方法强调了编译器报错的重要性以及对类型转换的理解和处理。
摘要由CSDN通过智能技术生成

目录

1. 二异性问题的解决

2. 关于字符串分配内存时字符串长度计算的函数调用问题

致谢


1. 二异性问题的解决

  • 之前的问题,我们重新回过头来看一看这个代码和他相应的报错,除去很多迷惑性的报错,仅看重点部分其实只有一句:

 note: candidate: ‘operator[](long int, const char*)’ (built-in)
  • 他就是说你在传入的量是一个字符串常量的时候他有两种方式可以解释他,第一种是const char*本身,即指向字符串常量第一个字符的位置的指针,第二种就是认为是const string类型,即我们的真实想法,这是编译器爆出来的歧义的根本原因。

  • 此外编译器爆出了一些很奇怪的报错,让我一直觉得这个bug来自于我的类型转换函数的重载,类似与下面这种,然后她在这里说了一个什么ambiguous。导致我没有往下细看。这种报错以及报错的时机让我坚信是我的重载类转函数有了问题。

 error: ambiguous overload for ‘operator[]’ (operand types are ‘Boy’ and ‘const char [7]’)
    89 |               << "\t salary: " << boy1[SALARY_KEY]
       | 
                                      ^
  • 再回过头来看我们的定义,以及我们的测试代码:

  • 宏定义如下:

 //using macro define
 #define SALARY_KEY "salary"
 #define AGE_KEY "age"
 #define POTENTIAL_KEY "potential"
 #define ID_KEY "id"
 #define SCORE_KEY "score"
  • []重载函数定义如下:

public:
     [[nodiscard]]int operator[](const std::string& id) const;
  • 源代码如下:

 int Boy::operator[](const std::string &id) const {
     if(id == "age"){
         return this->age_;
     }else if(id == "salary"){
         return this->salary_;
     }else if(id == "potential"){
         return this-> potential_;
     }else if (id == "id") {
         return static_cast<int>(this->id_);
     } else if(id == "score"){
         return this->evaluation();
     }
     return -1;
 }
  • 测试代码中相关的部分如下:

void test_4_squre_brackets(){
	Boy boy1("BIGBEAN", 26, 1000, 10);
    std::cout << "USING STRING INDEX: " << std::endl;
    std::cout << boy1.getName() << " >>> "
              << "\t salary: " << boy1[SALARY_KEY]
              << "\t age: " << boy1[AGE_KEY]
              << "\t potential: " << boy1[POTENTIAL_KEY]
              << "\t id: " << boy1[ID_KEY]
              << "\t score: " << boy1[SCORE_KEY]
              //here since the type conversion is defined aiming on avoiding the ambiguous,
              // the explicit conversion should be used here
              << "\n if undefined: " << boy1[std::string("undefine")] << std::endl;
    //......
}

解决

  • 所以问题就在于,我们定义的宏实质上是一个常量字符串,你可以把它看作是string字符串,也就是我们定义的函数,但是你也可以把它认为是一个字符串指针,返回的是这个指向字符串的首地址的指针,这会造成未定义的结果,这也就是歧义的原因(我又重复废话了一遍,不知道大家有没有懵掉,我本身也还是懵懵懂懂)。

  • 解决办法有很多,其实我上个文章说的解决办法并没有实质性地解决问题,那个只能算是回避问题,他只是规避了编译器的报错。

  • 我的解决方法(Rock 老师给我的建议)就是不要让编译器有这种错觉,直接在定义的时候强行指定他是一个const string 类型,而不是宏定义。

 //using macro define
 //#define SALARY_KEY "salary"
 //#define AGE_KEY "age"
 //#define POTENTIAL_KEY "potential"
 //#define ID_KEY "id"
 //#define SCORE_KEY "score"
 ​
 //using const std::string define
 const std::string SALARY_KEY = "salary";
 const std::string AGE_KEY = "age";
 const std::string POTENTIAL_KEY = "potential";
 const std::string ID_KEY = "id";
 const std::string SCORE_KEY = "score";
  • 原来的那个强制转换类型也不需要修改了改回去就好了,其实explicit 是一个新特性,这么写很容易造成代码的不可移植性(老版本编译器不支持)

 public:    
     operator int () const;
     operator char* () const;
  • 最后我们再来重新运行一下这个程序看一下报错吧,我就只运行[]转换函数,和int(), char*()的转换函数吧

  • (重复了,还是再看一遍吧)测试[]重载的函数如下:

 void test_4_squre_brackets(){
     Boy boy1("BIGBEAN", 26, 1000, 10);
     std::cout << "USING STRING INDEX: " << std::endl;
     std::cout << boy1.getName() << " >>> "
               << "\t salary: " << boy1[SALARY_KEY]
               << "\t age: " << boy1[AGE_KEY]
               << "\t potential: " << boy1[POTENTIAL_KEY]
               << "\t id: " << boy1[ID_KEY]
               << "\t score: " << boy1[SCORE_KEY]
               //here since the type conversion is defined aiming on avoiding the ambiguous,
               // the explicit conversion should be used here
               << "\n if undefined: " << boy1[std::string("undefine")] << std::endl;
 ​
     std::cout << "USING ENUMERATOR INDEX: " << std::endl;
     std::cout << boy1.getName() << " >>> "
               << "\t salary: " << boy1[SALARY]
               << "\t age: " << boy1[AGE]
               << "\t potential: " << boy1[POTENTIAL]
               << "\t id: " << boy1[ID]
               << "\t score: " << boy1[SCORE]
               << "\n if undefined: " << boy1[std::string("undefine")] << std::endl;
 }
  • 测试强制转换运算符的函数如下:(注意这里不用显示转换了)

 void test_4_type_conversion_operator(){
     Boy boy("BIGBEAN", 26, 1000, 10);
     int power = boy;
     char* name = boy;
 //    int power = int(boy);
 //    char* name = static_cast<char*>(boy);
     std::cout << name << ": " << power;
 }
  • 运行结果,完美运行!

 /media/herryao/81ca6f19-78c8-470d-b5a1-5f35b4678058/work_dir/Document/computer_science/QINIU/projects/week02/day03/project01/cmake-build-debug/project01
 USING STRING INDEX: 
 BIGBEAN >>>      salary: 1000    age: 26     potential: 10   id: 1   score: 84000
  if undefined: -1
 USING ENUMERATOR INDEX: 
 BIGBEAN >>>      salary: 1000    age: 26     potential: 10   id: 1   score: 84000
  if undefined: -1
 BIGBEAN: 84000
 Process finished with exit code 0

2. 关于字符串分配内存时字符串长度计算的函数调用问题

  • 除此以外rock老师也提到了另外一个代码的错误,就是我源代码在进行内存分配时使用了sizeof() 而不是strlen()。

  • 那么这二者的区别在哪里呢?

    • sizeof 是一个编译时操作符,用于确定变量或者数据类型在内存中所占字节的大小。对于字符换来说,他返回的是整个字符串的内存大小包含了'\0'.

    • strlen 他是一个运行时的函数,用于计算字符串的实际长度,不包含最后的休止符'\0',由于他的动态性,这显然更适合于我们的内存分配,如果一个动态的字符串被sizeof查看,那返回的值是在编译时确定的,这个有出问题的风险。

  • 所以为什么推荐使用strlen呢?

    • 首先我的源代码(大家可以看我上一篇的所有的构造函数中的前几行)使用了sizeof()+1这个本意显然是想包含最后的休止符,而sizeof本身就包含了这个,也就是我们多分配了一个字节的内存,可以说这种做法不算错,但是这绝对不是我的本意,所以他是不合适的。

    • 其次strlen是动态运行的,对于动态分配的字符串或者指针指向的字符串,sizeof 无法正确返回字符串的长度,只会返回指针本身的大小。而 strlen 可以正确计算出字符串的长度。

致谢

  • 感谢Rock 老师的回答哈,一边吃饭一边跟我语音聊的,就不放聊天记录了,还能听见老师在嚼东西哈哈哈。

  • 还有一点就是,编译器没暴露出来的错误不代表就没有风险,而且这种错误的理解要适可而止,编译器也会产生很多垃圾信息,作为初学者如果面面俱到很可能会造成进入死胡同,这便得不偿失。

  • 还有借用蒋炎炎老师的一句话,编译器永远是对的,没经过调试的代码一定是错的。

  • 此外就是我决定换一个封面,Rock老师也推荐我有一点自己的特色。

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值