跟我学C++中级篇——重载问题分析之常见的情况

ModelEngine·创作计划征文活动 10w+人浏览 1.4k人参与

一、重载的常见问题及说明

在重载的应用过程中,有一些很常见的重载问题。其中对运算符的重载、类成员的重载和模板相关的重载会专门进行分析处理。本文将会对其它一些重载问题进行一下总结:

  1. cv限定符的重载问题
    其实带cv限定符容易出错的问题在于开发者经常性的会忽视这种影响,这才是最主要的原因。如果无法抓住这个细节,在开发时会遇到一些小苦头。但这种问题也比较容易解决,一般发现调用的结果不正确的情况下就会反应过来。
  2. 作用域对重载的影响
    这种情况一般是新手或对重载知识的基础掌握不太扎实的情况下,属于容易犯但也比较容易解决的问题。重载不会在多个作用域内产生,如果出现不同作用域内的同名函数,结果往往是内部函数隐藏了外部函数。这个有点类似于父子类中的同名函数。
  3. 重载和默认参数的问题
    默认参数也是参数,但它的问题在于虽然可以产生重载,但由于默认参数的存在,重载和默认参数的调用会产生冲突,让编译无法确定到底是选择哪个函数。此时就需要开发者显示的处理,一般在开发时尽量避免这种情况。
  4. 匹配优先级的问题
    这种也属于对基础掌握不牢,但更倾向于具体的细节的问题。这种问题轻则重载不正确,严重则会导致递归调用引起崩溃
  5. 重载中的数组与指针问题
    大家都知道,数组在传参时,有可能退化成为指针。这个对开发者来说大概率是大意了
  6. 重载中普通函数和模板函数的问题
    重载中普通函数的优先级要高于模板函数,这很多开发者都清楚。但有的时候如果模板函数反而能够匹配更优的版本,则可能调用模板函数。切不可大意。另外,还要区分函数模板的特化,虽然这种特化不参与重载,但有可能让开发者写出意想不到代码并产生非预期的效果。
  7. 重载与可变参数问题
    这个问题属于一个提醒的问题,特别是在较多的类型重载的情况下,有些开发者可能为了省事,搞一个可变参数的,这样可能就会出现一些非预期的行为。它只会增加一些小烦恼,一般不会有什么让开发者很难受的问题出现。

需要说明的是,重载的问题往往与其它一些问题混合在一起,大家要明白其中的区别和联系。相关的例程见下面的解决方法中。

二、分析和解决

针对上面提供的问题,可以通过下面的例子进行对照分析和应用相关的解决方法:

  1. cv限定符的重载问题
void print(int value) {
    std::cout << "call print!" << std::endl;
}

//无法重载
void print(const int value) {
    std::cout << "call const print "<< std::endl;
}
//无法重载
void print(const int& value) {
    std::cout << "call const reference print "  << std::endl;
}
void print(int *ptr) { std::cout << "call point print! " << std::endl; }
void print(const int *ptr) { std::cout << "call const point print " << std::endl; }

void TestConstFunc() {
  int a = 0;
  print(a);
  // print((const int)7);
  print(&a);
  print((const int *)&a);
}

上面的代码大家认为有什么问题没有?可以上机试一下。在C++标准中函数参数中顶层const在函数签名的参数的处理中往往被忽略,所以上面有两个函数是无法形成重载的。但底层的const如指针或引用是可以形成重载的。大家可以看作他们是不同的类即可。这种基本功的问题,只有掌握重载的基础知识才能解决。
至于const在类中的常函数重载应用,会在类成员重载中进行分析说明。

  1. 作用域对重载的影响
void fun(int);  

class Demo {
    void fun(double);  

    void test() {
        fun(3); // 调用类内的fun(double)
        // 显示的使用::f(1),才可调用全局的fun函数
    }
};

要注意作用域的隐藏问题,这些往往对于新手来说是有可能被忽略的。特别是不同名空间的问题,这个还是要注意。尽量把需要重载的函数放到一起,不要想当然的写代码。
3. 重载和默认参数的问题

void printDefault(int a) { std::cout << "call one args printDefault! " << std::endl; }
void printDefault(int a, int b = 0) { std::cout << "call defalut value printDefault " << std::endl; }
void TestDefault() { printDefault(1); }//编译出错

上面的代码会出现编译问题“Call to ‘printDefault’ is ambiguous”。这种细节的小问题,很容易发现并处理。但一般不建议代码中出现类似的函数处理机制即不要在重载函数中出现默认值。
4. 匹配优先级的问题
这个是个较大的问题,一个不小心就可能出现大问题:

void printTransfer(const std::string &value) { std::cout << "call end!" << std::endl; }
void printTransfer(int value) { printTransfer(std::to_string(value)); }
void printTransfer(bool value) { printTransfer(value ? "true" : "false"); }
void TestPriority() {
  printTransfer(true); // 递归调用自身并导致栈溢出,崩溃
}

说明:代码崩溃的原因在于printTransfer(value ? “true” : “false”)后产生重载的优先级匹配,有两个选择:string版本和bool版本。而参数则为"true",自动退化为const char*,而char*转为bool是一个标准转换,但转成std::string则为库用户定义转换(库也是用户定义)。这样就会不断调用自身,导致栈空间耗尽,最终崩溃。
非预期的重载可能会导致崩溃的现象,就如上面的代码一样,所以这类问题一定要进行显示的控制即精确的进行函数匹配定义。这种问题解决的方式非常简单,就是要显式的重载所有的数据类型,或者显式的进行数据类型转换。当然使用其它的能够显式的给编译器指明调用方向的都可以,目的只有一个,不让编译器替开发者做决定。
5. 重载中的数组与指针问题

void funArray(int *){};
void funArray(int[]){};
void funArray(int[10]){};
void funArray(int (&arr)[10]);//应该可以重载,但实际仍然无法实现重载
void TestArray() {
  int a[10];
  funArray(a);
}

这种指针退化的问题很容易发现,解决方法就是避免这种应用。
6. 重载中普通函数和模板函数的问题

template <typename T> void funTemp(T) { std::cout << "call template func!" << std::endl; }
void funTemp(double) {}

void TestTemp() { funTemp(3); }

虽然说一般情况下普通函数的匹配要高于模板函数,但前提是它们必须是在同等的情况下。比如上面的代码就会调用模板函数,因为3做为int类型直接可以匹配模板的实例,而double类型参数的普通函数需要转换,那么就优先级低了。
解决方法仍然是显式的处理想要处理的数据类型,或者干脆不使用这种重载方式。
7. 重载与可变参数问题

void print(const char* message);        
void print(int count, ...);        

print("exp");  // 调用重载 
print(3,2,1);  // 调用可变参数 

这种情况下,重载的优先的优先级要高一些,但前提是匹配要更好一些。如果完全相同,则类似顶层的const参数机制一样,会导致无法确定函数的调用。所以在实际开发中,应尽量杜绝这种写法。如果是与其它人合作,则需要显示的确定调用哪个即确定更高的匹配度。另外,还有一种是可变参数模板的情况,会在模板相关的重载分析中说明

通过代码的分析找到问题的症结,那么解决的方式就是通过代码中的反馈有针对性的进行处理即可。

三、总结

重载本身其实并没有多么艰深的技术问题,但往往问题产生在细节之间。这些技术细节交织着各种基础的技术知识点。往往一个大意或者了解不清楚就会引发各种问题。重载的问题相对于其它问题还是比较好发现和解决的。所以,从问题本身反馈进行基础知识的学习和复习,可能更容易让开发者理解并加深记忆。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值