我在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());
}
答案 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个优点:
资源管理是自动进行的,因此我们无法删除CaloGrid
,或意外删除它两次。
量热仪继承了智能指针的复制/移动功能(在这种情况下,不允许使用危险的不需要的副本,但我们可以保留移动和移动分配)
此外,尽管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
数据成员公开。