The*_*ist 14 c++ pointers casting const const-cast
我一直听到这句话,但我无法真正找到const_cast是邪恶的原因.
在以下示例中:
template <typename T>
void OscillatorToFieldTransformer<T>::setOscillator(const SysOscillatorBase<T> &src)
{
oscillatorSrc = const_cast<SysOscillatorBase<T>*>(&src);
}
Run Code Online (Sandbox Code Playgroud)
我正在使用引用,并使用const,我保护我的引用不被更改.另一方面,如果我不使用const_cast,代码将无法编译.为什么const_cast在这里不好?
这同样适用于以下示例:
template <typename T>
void SysSystemBase<T>::addOscillator(const SysOscillatorBase<T> &src)
{
bool alreadyThere = 0;
for(unsigned long i = 0; i < oscillators.size(); i++)
{
if(&src == oscillators[i])
{
alreadyThere = 1;
break;
}
}
if(!alreadyThere)
{
oscillators.push_back(const_cast<SysOscillatorBase<T>*>(&src));
}
}
Run Code Online (Sandbox Code Playgroud)
请给我一些例子,我可以看到使用const_cast是一个坏主意/不专业.
谢谢你的任何努力:)
Set*_*gie 32
因为你挫败了目的const,这是为了阻止你修改论证.因此,如果你抛弃了const某些东西,它会使你的代码变得毫无意义和膨胀,并且它会让你违背你对函数用户所作的不会修改参数的承诺.
此外,使用const_cast可能会导致未定义的行为.考虑以下代码:
SysOscillatorBase<int> src;
const SysOscillatorBase<int> src2;
...
aFieldTransformer.setOscillator(src);
aFieldTransformer.setOscillator(src2);
Run Code Online (Sandbox Code Playgroud)
在第一次通话中,一切都很好.你可以丢弃const一个不是真正的对象的ness const并修改它.但是,在第二次调用中,setOscillator你正在抛弃const一个真正的const对象.如果您碰巧在任何地方修改了该对象,则通过修改实际对象来导致未定义的行为const.既然你无法判断一个被标记的对象const是否真的 const被声明了,那么const_cast除非你确定永远不会改变对象,否则你应该永远不要使用它.如果你不愿意,有什么意义呢?
在您的示例代码中,您正在存储一个非const指针,该指针可能是一个对象,const表示您打算改变该对象(否则为什么不将指针存储到const?).这可能会导致未定义的行为.
此外,这样做可以让人们将临时函数传递给您的函数:
blah.setOscillator(SysOscillatorBase<int>()); // compiles
Run Code Online (Sandbox Code Playgroud)
然后你存储一个指向临时的指针,当函数返回1时它将无效.如果您采用非const引用,则不会出现此问题.
另一方面,如果我不使用const_cast,代码将无法编译.
然后更改代码,不要添加强制转换以使其工作.编译器没有出于某种原因编译它.既然你知道了原因,那么你就可以vector把握住指针,const而不是将一个方孔投入圆形孔中以适合你的钉子.
所以,在各地,最好让你的方法接受非const引用,而使用const_cast几乎不是一个好主意.
1实际上,当调用函数的表达式结束时.
Pot*_*ter 10
通过使用const,我保护我的引用不被更改
引用无法更改,一旦初始化它们总是引用同一个对象.引用const意味着它所引用的对象无法更改.但是const_cast,断言并允许对象进行更改.
另一方面,如果我不使用const_cast,代码将无法编译.
这不是任何理由.C++拒绝编译可能允许const更改对象的代码,因为这意味着const.这样的程序是不正确的.const_cast是一种编译错误程序的方法 - 这就是问题所在.
例如,在您的程序中,看起来您有一个对象
std::vector< SysOscillatorBase<T> * > oscillators
Run Code Online (Sandbox Code Playgroud)
考虑一下:
Oscillator o; // Create this object and obtain ownership
addOscillator( o ); // cannot modify o because argument is const
// ... other code ...
oscillators.back()->setFrequency( 3000 ); // woops, changed someone else's osc.
Run Code Online (Sandbox Code Playgroud)
通过const引用传递一个对象不仅意味着被调用的函数不能改变它,而且该函数不能将它传递给可以改变它的其他人.const_cast违反了这一点.
C++的优势在于它提供了保证所有权和价值语义的工具.当您禁用这些工具以使程序编译时,它会启用错误.没有好的程序员认为可以接受.
作为这个特定问题的解决方案,看起来vector(或者你正在使用的任何容器)应该按值存储对象,而不是指针.然后addOscillator可以接受const引用,但存储的对象是可修改的.此外,容器然后拥有对象并确保它们被安全删除,您无需做任何工作.
const_cast出于任何原因使用除了适应(旧)库,其中接口具有非常量指针/引用但实现不修改参数是错误和危险的.
它是错误的原因是因为当您的接口获取引用或指向常量对象的指针时,您承诺不更改该对象.其他代码可能取决于您不修改对象.例如,考虑一种类型,它拥有一个昂贵的复制成员,并且它与其拥有一些其他不变量.
考虑a vector<double>和预先计算的平均值,每当通过类接口添加新元素时都会更新*average,因为当时更新便宜,并且如果经常请求它,则不需要每次都从数据中重新计算它.由于向量的复制成本很高,但可能需要读取访问权限,因此类型可以提供便宜的访问器,该访问器返回std::vector<double> const &用户代码以检查容器中已有的值.现在,如果用户代码抛弃了引用的常量并更新了向量,则类保持平均值的不变量将被破坏,程序的行为将变得不正确.
这也很危险,因为您无法保证您传递的对象实际上是可修改的.考虑一个简单的函数,它接受一个C null终止的字符串并将其转换为大写,足够简单:
void upper_case( char * p ) {
while (*p) {
*p = ::to_upper(*p);
++p;
}
}
Run Code Online (Sandbox Code Playgroud)
现在让我们假设您决定更改接口以获取a const char*,并执行以删除const.使用旧版本的用户代码也适用于新版本,但现在在编译时将无法检测到某些将在旧版本中标记为错误的代码.考虑到有人决定做一些愚蠢的事情upper_case( typeid(int).name() ).现在问题是结果typeid不仅仅是对可修改对象的常量引用,而是对常量对象的引用.编译器可以自由地将type_info对象存储在只读段中,并使用加载器将其加载到只读内存页中.尝试更改它会使程序崩溃.
请注意,在这两种情况下,您无法从上下文中了解const_cast是否维护了额外的不变量(案例1),或者即使对象实际上是常量(案例2).
另一方面,const_cast存在的原因是适应不支持const关键字的旧C代码.一段时间以来,函数strlen会采用a char*,即使已知并记录该函数不会修改对象.在这种情况下,可以安全地使用const_cast以适应类型,而不是改变常量.请注意,C已经支持const了很长时间,并且const_cast具有较少的正确用途.