使用const_cast重载常量函数时出现分段错误

时间:2016-12-23 14:31:55

标签: c++ segmentation-fault const-cast

我在const_cast功能方面遇到了一些问题。我创建了一个由Calorimeter和其他一些东西组成的类CaloGrid。我必须重载grid()函数以返回属于类CaloGrid的{​​{1}},但是调用main函数会返回分段错误。

我知道使用Calorimeter不是最好的做法,但对于这项任务,我必须使用它。只需复制非常量函数const_cast的代码就可以了。

我做错了什么?有没有更好的方法呢?使用函数的const CaloGrid& grid() const副本重载函数有什么意义?

的main.cpp

const

Calorimeter.cpp

/*headers*/
int main() {
// create 2x2 Calorimeter object
Calorimeter C(2,2);
// return the CaloGrid class from this object
C.grid();
// gives segmentation error
}

Calorimeter.hh

/*headers*/

// Calorimeter is object with CaloGrid of dimensions nx by ny
Calorimeter::Calorimeter(int nx,int ny){
// initalize the grid and allocate memory
    Cgrid = new CaloGrid(nx,ny);
}

Calorimeter::~Calorimeter(){
// delete memory
    delete Cgrid; 
}

// return the grid
const CaloGrid& Calorimeter::grid() const{
    return *Cgrid;
}

// segmentation error
CaloGrid& Calorimeter::grid(){
    return const_cast<CaloGrid&> (Calorimeter::grid());
}

5 个答案:

答案 0 :(得分:4)

return const_cast<CaloGrid&> (Calorimeter::grid());

你无限期地打电话给grid()。由于grid函数是非const Calorimeter::grid(),因此将再次调用函数的非const版本,再次调用非const版本,这就是你得到的结论。

如果您想调用该函数的const版本,则需要将this投射到const。你可以用

做到这一点
const_cast<const Calorimeter&>(*this)

因此,您的完整代码看起来像

return const_cast<CaloGrid&>(const_cast<const Calorimeter&>(*this).grid());

如果它看起来不正确,可能是眼泪从你的代码中涌入。

答案 1 :(得分:3)

这是你的班级方法:

CaloGrid& Calorimeter::grid(){

它做什么?良好:

return const_cast<CaloGrid&> (Calorimeter::grid());

它会调用Calorimeter::grid(),并将const_cast应用于其返回值吗?这Calorimeter::grid()做了什么?见上文。

const_cast所做的事情,以及它是否正确的做法是无关紧要的。这个类方法调用自身,导致无限递归,并且程序快速爆发,因为它耗尽了操作系统分配的堆栈空间。

虽然你在这里尝试做什么并不是很清楚,但是关于你的段错的原因的答案很简单:无限递归。

递归调用不会调用另一个重载的const类方法。这是从一个可变类方法调用的,因此它再次选择了可变重载。

答案 2 :(得分:2)

扩展其他帖子,您可能需要考虑这样写:

#include <memory>

struct CaloGrid {
  CaloGrid(int x, int y) {};
};

class Calorimeter {

public:    // Interface
    Calorimeter(int,int);

    // no destructor - it's not necessary

    const CaloGrid& grid() const;

    CaloGrid& grid();

private:   // Implementation

  // resources managed automatically    
  std::unique_ptr<CaloGrid> Cgrid;
};

// Calorimeter is object with CaloGrid of dimensions nx by ny
Calorimeter::Calorimeter(int nx,int ny)
  : Cgrid { std::make_unique<CaloGrid>(nx, ny) }
{
}

// return the grid
const CaloGrid& Calorimeter::grid() const{
    return *Cgrid;
}

// no error any more
CaloGrid& Calorimeter::grid(){
    return *Cgrid;
}

int main() {
    // create 2x2 Calorimeter object
    // now we can use move-construction
    auto C = Calorimeter(2,2);

    // return the CaloGrid class from this object
    C.grid();
}

原始指针已被智能指针替换。这给了我们(至少)2个优点:

  1. 资源管理是自动进行的,因此我们无法删除CaloGrid,或意外删除它两次。

  2. 量热仪继承了智能指针的复制/移动功能(在这种情况下,不允许使用危险的不需要的副本,但我们可以保留移动和移动分配)

  3. 此外,尽管grid方法现在重复代码,但它重复了一些简单的代码。该课程更容易正确使用和维护。

答案 3 :(得分:0)

您需要的是以下

CaloGrid & Calorimeter::grid()
{ 
    return const_cast<CaloGrid &>(const_cast<const Calorimeter *>(this)->grid());
                                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^                 
}

否则非常量成员函数网格以递归方式调用自身。

答案 4 :(得分:0)

  

我在const_cast功能方面遇到了一些问题。

它是一个操作员,而不是一个功能。

  

我知道使用const_cast不是最佳做法,

如果您设计代码的架构使const_cast无法避免,那么这是不好的做法。如果在其他地方已经违反了const的正确性,那么它可以是一种可接受的真实解决方法(例如,在您必须使用的库中,尽管精心设计的库不会受到这些缺点的影响)。

  

但是对于这项任务,我必须使用它。

说谁?

  

简单地复制非常量函数的const CaloGrid& grid() const的代码可能会有效。

是的,这是最好的做法。就这么做。

  

我做错了什么?

你有点太急于申请DRY(不要重复自己)原则。这使你编写的代码在另一个答案中意外地引起了Sam解释的无限递归问题。

  

使用函数的const副本重载函数有什么意义?

const重载允许您在Calorimeter const对象上调用该函数。

它就像std::vector::operator[]一样,仅举一个突出的例子。像这样的操作通常是const / non-const对。

您可以查看编译器如何实现std::vector::operator[]。它很有可能只复制必要的一小段代码,使用const_cast或其他技巧。

顺便说一句......

首先拥有像grid()这样的函数可能不是一个好主意。它实际上使private数据成员公开。