【问题标题】:Might this cause an infinite loop?这会导致无限循环吗?
【发布时间】:2016-05-01 05:28:58
【问题描述】:

对于定义函数的第二个const 版本,这样做是否保证安全?看起来它会无限递归,因为我想返回 const 但我要调用的另一个函数是非常量。

它适用于 g++,但我担心这是不安全的。

#include <iostream>

using namespace std;

class test {
public:
   int* doSomething(int a) {
      int* someInt = new int(a);

      return someInt;
   }

   const int* doSomething(int a) const {
      return doSomething(a);
   }
};

int main() {
   test a;

   cout << *a.doSomething(12345) << endl;

   return 1;
}

【问题讨论】:

  • 这段代码调用了doSomething的非常量版本。尝试调用 const 版本,然后看着它爆炸。
  • 分配时为什么不初始化someIntint* someInt = new int(a);
  • 我一直认为 c++ 不允许重载仅返回类型不同的函数......
  • @Anedar:重载不仅在返回类型上有所不同。它也有不同的 cv-qualification。
  • @Anedar - 它没有。但这里不是这样。这两个函数的 const 限定符不同,它决定了哪个可以在 const 对象上调用,哪个可以在非常量对象上调用。

标签: c++ recursion constants const-method


【解决方案1】:

不完全是:正如@Pete Becker 在 cmets 中指出的那样,如果您调用了 递归的 const 版本:

class test {
public:
   int* doSomething(int a) {
      int* someInt = new int;
      *someInt = a;
      return someInt;
   }

   const int* doSomething(int a) const {
      return doSomething(a);
   }
};

int main() {
   const test a;
   // You're not in for a good time:
   a.doSomething(12345);
   return 1;
}

当提供需要重复代码的函数的const和非const版本时,最好实现const版本,然后让非const版本以特定方式调用它。

来自斯科特迈尔斯Effective C++ - Third Edition

const 和非const 成员函数具有基本相同的实现时,可以通过让非const 版本调用const 版本来避免代码重复

Scott Myers 继续为此提供了一种安全的方法:

const int* doSomething(int a) const
{
   int* someInt = new int;
   *someInt = a;
   return someInt;
}

int* doSomething(int a)
{
   return const_cast<int*>(static_cast<const Test&>(*this).doSomething());
}

在非const 版本中,有两种类型转换:static_cast 基本上将this 转换为const this,其中const_cast 丢弃了返回的const-ness。这样做是安全的,因为要调用非const 版本,您必须拥有非const this

但是,如果您只是提供对成员的访问权限,那么只需以下内容就更简单、更容易阅读:

class TestAccess;
class Test
{
    TestAccess& t;
public:
    const TestAccess& getA() const { return t; }
    TestAcess& getA() { return t; }
};

【讨论】:

  • Scott Myers 的解决方案非常完美。我试图考虑是否有一种方法可以从const 以某种方式调用非const 版本,我认为你不能。感谢您清楚地解释这一点。部分问题是我误解了 const 方法的工作原理:stackoverflow.com/questions/34983475/…
  • 有什么注意事项让const 版本调用非const 版本?似乎工作正常。
  • 是的:const 成员函数承诺永远不会更改对象的状态,但您实际上是在调用可以更改对象状态的非const 函数。让非const 版本调用const 版本更安全,因为非const 函数可以对对象做任何事情。
  • 你是对的,但从简单地删除重复代码的角度来看,它不应该有所作为。我做了这样的事情:u8char_t* codepoint = &amp;_base[0]; 从 const 方法调用时会引发错误,除非我使用静态强制转换删除 const。我觉得如果我这样做,那么我还不如从const 中调用非const 版本并放弃演员表。澄清一下,我使用codepoint 使用codepoint + length 迭代基本字符串,其中length 是utf-8 字符的宽度(以字节为单位)。
【解决方案2】:

在这种情况下,编译器总是会选择函数的非 const 版本,甚至不会调用 const 版本。 否则编译器将无法编译,你正在刹车。 比如我快速修改了代码:

#include <iostream>

using namespace std;

class test {
public:
    int* doSomething(int a) {
        int* someInt = new int;

        *someInt = a;
        return someInt;
    }
    int ax = 10;
    void somethingElse(int i)
    {
        ax = i;
    }
    const int* doSomething(int a) const {
        somethingElse(a);
        return 0;
    }
};

int main() {
    test a;

    cout << *a.doSomething(12345) << endl;

    return 1;
}

此示例无法编译,因为您在 const 范围内调用 const 函数。编译器不会让你这样做。

现在,我知道这是一个测试,但是这样做你永远不会退出递归,它会永远循环,而且你在每次调用时都会通过在堆上分配来泄漏内存,这两件事加在一起会导致一场灾难。

【讨论】:

  • 我知道它正在泄漏内存。这只是一个例子。
  • 出于对您的编码风格的好奇,somethingElse() 的右括号是否出于某种原因缩进了 8 个空格,或者这是一个错误?
  • 您可以只返回 int 本身,即使对于测试也看不出为什么需要泄漏指针。反正不是重点。关于括号,是的,我很快就编码了,一定是在复制粘贴 Cheers M 时搞砸了。
  • G++ 对我大喊大叫说warning: type qualifiers ignored on function return type 所以我把它改成了指针。它可以是 const 但结果是本机类型并且总是按值传递;所以const 没有实际意义。
  • 您可以将指针和引用传递给本机类型。但通常,除非有充分的理由,否则你应该尽量避免它,因为你使用的内存比你应该使用的多,在 64 位机器上,指向 int 的指针或引用是 64 位的,同时 int 本身很可能是32位。但是,如果您有多个用于返回值的参数,那么您可以使用 ref 或指向本机类型的指针。
猜你喜欢
  • 2015-05-20
  • 2013-06-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多