【发布时间】:2020-02-16 03:46:50
【问题描述】:
我从std::runtime_error派生了一个自定义异常类
静态分析器给我一个警告,如果我定义或删除默认操作(复制 ctors、复制/移动操作符、析构函数等),我应该定义或删除它们。
为了解决这个愚蠢的警告,我写了缺少的赋值运算符,但后来我收到另一个警告,现在我的运算符隐藏了基本的非虚拟赋值运算符!
由于基类有我无法复制的私有成员,看来唯一的解决方案是直接为基对象部分调用基赋值运算符,然后复制 *this 对象的其余部分并最终返回 *this
但在此之前,我查看了基础 operator= 的功能以及它的外观:
exception& operator=(exception const& _Other) noexcept
{
if (this == &_Other)
{
return *this;
}
__std_exception_destroy(&_Data);
__std_exception_copy(&_Other._Data, &_Data);
return *this;
}
private:
__std_exception_data _Data;
};
现在知道这是我的实现(使用 cmets)来调用基本分配并复制派生对象的其余部分:
class Exception :
public std::runtime_error
{
public:
// ...
Exception& operator=(const Exception& other)
{
if (this == &other)
{
return *this;
}
// first copy only base class data to *this
*dynamic_cast<std::runtime_error*>(this) =
runtime_error::operator=(
*dynamic_cast<std::runtime_error*>(
const_cast<Exception*>(&other)));
// then copy derived class data to *this
mInfo = other.mInfo;
mCode = other.mCode;
// finally return complete copy
return *this;
}
private:
std::error_code mCode;
std::string mInfo;
};
这是正确的方法吗?我认为这看起来很麻烦,但我不确定。
编辑
这是完整的课程,供参考:
#pragma warning (disable : 4275) // base needs to have DLL interface
class ERROR_API Exception :
public std::runtime_error
{
public:
~Exception() noexcept; // cant be inlined in release build
// default/delete
Exception(const Exception&) = default;
Exception(Exception&&) = delete;
Exception& operator=(const Exception& other)
{
if (this == &other)
{
return *this;
}
// copy base class data to *this
*dynamic_cast<std::runtime_error*>(this) =
runtime_error::operator=(
*dynamic_cast<std::runtime_error*>(
const_cast<Exception*>(&other)));
// copy derived class data to *this
mInfo = other.mInfo;
mCode = other.mCode;
return *this;
}
Exception& operator=(Exception&&) = delete;
/** Construct from error enum */
template<typename Enum>
Exception(Enum err_enum);
/** Construct from error enum and string*/
template<typename Enum>
Exception(Enum err_enum, String message);
/** Construct from error_code object */
inline Exception(std::error_code err_code);
/** Construct from error_code object and string */
inline Exception(std::error_code err_code, String message);
/** Get error_condidtion name */
inline virtual std::string ConditionName() const;
/** Get error_category name */
inline virtual std::string CategoryName() const;
/** Get error_condition value */
inline virtual int ConditionValue() const noexcept;
/** Get error_condition value */
inline virtual int ErrorValue() const noexcept;
/** Get additional information string passed to constructor */
inline virtual const String& GetInfo() const noexcept;
/** Get error_code object associated with this exception object */
inline virtual const std::error_code& code() const noexcept;
private:
SUPPRESS(4251); // member needs to have DLL interface
std::error_code mCode;
SUPPRESS(4251); // member needs to have DLL interface
String mInfo;
};
#pragma warning (default : 4275) // base needs to have DLL interface
【问题讨论】:
-
那么为什么需要实现或删除任何特殊成员呢?这看起来像是一个很好的零规则。
-
请在开始添加内容之前发布原始课程,只是基础知识,也许它可以按原样进行,并且您的静态分析器(哪个?)过于激进。
-
首先,静态分析器为您提供了两种可能的方法来解决此问题。我想你选错了,删除这些是我的选择。也就是说,所有 dynamic_cast 的东西都不是必需的,只要记住
operator=也可以作为方法调用,所以你只需调用runtime_error::operator=(other)来分派到基类。 -
如果您打算实现其他默认操作,则让编译器定义它们,直到您准备好定义自己的操作。最多将它们定义为您可以在以后完成的存根。无论如何,隐藏警告可能是因为您的
operator=()与继承的不一致。std::exceptionsoperator=()具有throw()规范(C++11 之前)或noexcept(C++11 及更高版本)。您的派生类需要与之保持一致 - 否则您的operator=()会根据标准隐藏基类版本。 -
鉴于您的静态分析器不同于编译器及其库(这是定义
std::exception和派生类的地方),您可能对此无能为力。只需记录静态分析器发出警告的事实,并且您已经确定它是可以接受的(即误报)。如果您的静态分析器支持此类事情,请在Exception类中添加注释,以防止静态分析器发出警告。您需要阅读静态分析器的文档以确定这是否可行,如果可行,如何实现。
标签: c++ inheritance assignment-operator