C++ C++;:纯虚拟赋值算子

C++ C++;:纯虚拟赋值算子,c++,linker-errors,operator-keyword,pure-virtual,C++,Linker Errors,Operator Keyword,Pure Virtual,为什么如果我们在基类中有纯虚拟赋值运算符,那么我们在派生类上实现该运算符,它会在基类上给出链接器错误 目前我只有以下解释,它说行为是设计的,因为正常的继承规则不适用 我不清楚,为什么设计时会产生链接器错误?有人能给我更清楚的解释吗 编辑:添加了发生错误的我的简化代码: class __declspec(dllexport) BaseClass { public: int memberA; virtual BaseClass& operator=(const BaseCla

为什么如果我们在基类中有纯虚拟赋值运算符,那么我们在派生类上实现该运算符,它会在基类上给出链接器错误

目前我只有以下解释,它说行为是设计的,因为正常的继承规则不适用

我不清楚,为什么设计时会产生链接器错误?有人能给我更清楚的解释吗

编辑:添加了发生错误的我的简化代码:

class __declspec(dllexport) BaseClass {
public:
    int memberA;
    virtual BaseClass& operator=(const BaseClass& rhs) = 0;
};

class __declspec(dllexport) DerivedClass : public BaseClass {
public:
    int memberB;
    DerivedClass():memberB(0) {}
    virtual BaseClass& operator=(const BaseClass& rhs) {
        this->memberA = rhs.memberA;
        this->memberB = 1;
        return *this;
    }
};

int main(void)
{
    DerivedClass d1;
    DerivedClass d2;

    BaseClass* bd1 = &d1;
    BaseClass* bd2 = &d2;

    *bd1 = *bd2;
}
没有
\u declspec(dllexport)
和/或没有纯虚拟运算符=基类声明,代码编译时不会出现错误

分配
*bd1=*bd2后,无
\u declspec(dllexport)
,d1::memberB为1,但使用
\u declspec(dllexport)
d1::memberB保持不变


在分配
*bd1=*bd2之后,使用
\uuu declspec(dllexport)
,并且不使用纯虚拟声明,d1::memberB保持不变

运算符=未继承。你的代码在C++中是没有意义的,所以编译器可以自由地发布他们想要的任何错误。 从您指向的知识库文章中:

由于运算符=不是继承的,因此基类中运算符=的任何声明都是未使用的,也没有必要的。不要在基类中声明运算符=

这可能只是他们如何编译的副作用,他们只是让你知道他们不认为它是一个bug,所以没有必要修复它。“By design”并不一定意味着他们明确认为此链接器错误是针对这种情况给出的正确错误消息——代码是错误的,您会得到一个错误,因此从他们的角度来看——他们已经完成了。

在示例代码中:

class A
{
public :
   // To workaround LNK2001, comment the following line.
   virtual const A& operator=( const A& f ) = 0;
};

class B : public A
{
public :
   const A& operator=( const A& g ) {return g;}
};

B aB1, aB2;

int /*void*/ main( void )
{
   aB2 = aB1;
}

aB2=aB1
不调用
const A&B::operator=(const A&)
,而是调用自动提供的
B&operator=(const B&)
,它依次使用赋值运算符来赋值类的基部分。但当涉及到链接时,事实证明这从未实现。

来自标准第12.8节: 13类X的隐式定义的复制赋值运算符执行其子对象的成员式赋值。这个 X的直接基类首先按照它们在基说明符列表中的声明顺序分配,然后是立即基 X的非静态数据成员是按类定义中声明的顺序分配的。每个子对象 以适合其类型的方式分配:

-如果子对象为类类型,则使用该类的复制赋值运算符(如同通过显式限定; 也就是说,忽略更多派生类中任何可能的虚拟重写函数);


子类正在使用隐式定义的复制赋值运算符,基类的复制赋值运算符没有定义,但它是声明的,因此您会得到一个链接错误而不是编译错误。

发布一些示例代码以及MSVC正在吐出的完整链接器错误可能会有所帮助,Scott Meyers的更有效的C++(“使非叶类抽象”)的项目33涵盖了继承层次中如何处理运算符=。above@uray:理想情况下,关于SO的问题将是独立的,因为一个目的是为我们之后的人建立一个知识库。我看到microsoft.com上的东西会随着时间的推移而消失。@uray:除了David Thornley提到的以外,有时MS代码本身可能包含错误,或者只是太过泛化,以至于它不太适用于您的特定情况。我推断该示例来自于您的代码,其中可能有一个错误,MS没有。我想他说的是运算符=,不是复制构造函数。对于copy-ctor--virtual和pure-virtual应该是一个直接的编译器错误,因为这对他们来说真的没有意义。他的链接是关于operator=的,这就是我的文章的内容。从OP
开始,我们在派生类上实现该运算符,我要说,他在子类中有一个显式的copy-assignment运算符(不是暗示您的答案将正确涵盖的内容),也没有迹象表明它正在或没有调用父级(纯虚拟)副本分配。是的,但在注释中,他确实明确指出Necrolis指向链接。即使如此,如果他定义了
A&operator=(A&)
在B中,赋值仍将调用隐式版本,因为两个操作数都是类型B,并且“基类复制赋值运算符总是被派生类的复制赋值运算符隐藏”(12.8.10)。赋值运算符已被重写。与声明任何构造函数(非模板)一样有效地停用隐式定义的默认构造函数和复制构造函数,声明任何
运算符=
(非模板)停用隐式定义的赋值运算符。@Matthieu:这符合复制赋值吗?毕竟,
结构X{X&运算符=(int);};
不会阻止我将一个X分配给另一个X,如果
操作符=
的任何重载禁用了默认的X。-类似地,任何构造函数都不会停用复制构造函数,只是一个有效的用户定义的复制构造函数。您是对的,但是当使用
B&operator=(常量&)
调用的是这个版本,而不是默认版本(至少在VC++10中是这样)…现在我很困惑,现在研究这个已经太晚了。。。