【问题标题】:C++ Overloading Operator<< By Using Static Helper Methods Creating Access Violation ErrorC++ 重载运算符<< 通过使用静态辅助方法创建访问冲突错误
【发布时间】:2017-05-25 14:34:54
【问题描述】:

我有一个名为Bill 的课程。在类的属性中,有一个称为category 的类型为int。 打印账单时,应显示类别名称而不是编号。所以我写了这个静态辅助函数来将category整数转换为对应的字符串:

// Takes an integer representing the category and returns its corresponding name
static const std::string& getCategoryByNumber(int category)
{
    switch (category)
    {
    case 1:
        return "Food";
    case 2:
        return "Gift";
    case 3:
        return "Fuel";
    case 4:
        return "Electricity";
    case 5:
        return "Clothes";
    case 6:
        return "Holidays";
    case 7:
        return "Water";
    case 8:
        return "Fees";
    default:
        exit(1);
    }
}

现在我尝试在 Bill 类定义之外重载 operator&lt;&lt;,如下所示:

std::ostream& operator<<(std::ostream& os, Bill& bill) {
    int category = bill.getCategory();
    const std::string& nameOfCategory = getCategoryByNumber(category);
    std::cout << nameOfCategory.c_str();
    return os;
}

创建bill类型的对象并运行这行代码时出现错误: cout &lt;&lt; billObject;

如果这些信息还不够,我很乐意添加更多信息。 我该如何解决这个错误,是什么原因造成的?

【问题讨论】:

  • std::string&amp; -> std::string
  • 调试器是解决此类问题的正确工具。 询问 Stack Overflow 之前,您应该逐行浏览您的代码。如需更多帮助,请阅读How to debug small programs (by Eric Lippert)。至少,您应该 edit 您的问题包含一个重现您的问题的 Minimal, Complete, and Verifiable 示例,以及您在调试器中所做的观察。
  • @iehrlich 这样的东西可以用调试器检查。您从哪里假设 OP 是 一个没有经验的程序员
  • 离题:考虑用 std::string 数组替换那个开关。你可以return categoryStrings[category];(如果你不能为浪费的 0 元素负担一个字符串的 RAM,也许可以category-1)和一个确保类别在范围内的测试。
  • "Food" 不是string。它是恒定的字符数组。编译器将基于char 数组自动且静默地创建string,因为string 是必需的并且string 具有char 数组转换构造函数。不幸的是,这个制造的string 是一个自动变量(也称为本地变量或临时变量),并且会超出范围并几乎立即被销毁,并且肯定会在您有机会使用它之前被销毁。最终结果是调用者获得了对死变量的引用。

标签: c++ operator-overloading static-methods


【解决方案1】:

编译器应该已经警告你“返回对本地对象的引用”或类似的(如果你打开所有警告,你总是应该这样做)。你也应该避免调用exit(),而是throw一个异常给应用程序一个恢复的机会

struct Bill
{
  /* ... */
  int GetCategory() const;
  static string GetCategoryName(int category)
  {
    switch (category) {
    default: throw std::runtime_error("Bill: category '"+
                                      std::to_string(category)+"' unknown");
    case 1: return "Food";
    case 2: return "Gift";
    case 3: return "Fuel";
    case 4: return "Electricity";
    case 5: return "Clothes";
    case 6: return "Holidays";
    case 7: return "Water";
    case 8: return "Fees";
    }
  }
};

inline
std::ostream& operator<<(std::ostream&os, Bill const&bill)
{
  return os << Bill::GetCategoryName(bill.GetCategory());
}

【讨论】:

  • 支持例外。对于那些稍后阅读的人,不仅可以捕获和处理它,而且如果它没有被捕获,它通常会导致为用户打印出可用的、人类可读的错误消息。远比“为什么#%@*^ 程序崩溃了?”更令人沮丧?
【解决方案2】:

我对这里真正发生的事情的猜测是,编译器在 getCategoryByNumber 函数的上下文中从 C 字符串创建一个 std::string 对象,返回对它的引用,并且它引用的对象在函数返回。

请考虑从getCategoryByNumber返回const char*std::string而不是std::string&amp;

【讨论】:

  • 请注意,当函数结束时,将调用创建的临时字符串的析构函数。在它运行之后,对象的生命周期已经结束,并且在其生命周期结束后尝试使用对象是 UB。
  • @NathanOliver 这重复了我的回答,除了这句话。这句话在这里是有原因的 - 被破坏对象的即时解除引用可能会意外地起作用,例如,在调试器中,它使用比通常运行时更谨慎的内存布局。大约一周前发生在我身上。当您的应用程序在普通运行中失败,但在 GDB 下运行时,这真的很讨厌,对吧?
  • 我猜你的反对意见来自第一行和第三行,你应该把它们读一遍:)
  • 执行 return std::string("Food"); 与编译器自动为 OP 执行的操作完全相同,并将导致完全相同的行为。
  • @iehrlich “虽然实际上它返回 const char*。”不正确,该函数将返回 std::string&amp;return std::string("Food"); 不会有什么不同,你仍然会返回一个临时的引用。
猜你喜欢
  • 1970-01-01
  • 2010-11-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-05-14
  • 1970-01-01
  • 2014-04-15
  • 1970-01-01
相关资源
最近更新 更多