6

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

目前我在http://support.microsoft.com/kb/130486上只有以下解释,它说这种行为是设计使然,因为正常的继承规则不适用

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

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

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;
}

如果没有 __declspec(dllexport)和/或没有基类上的纯虚拟运算符 = 声明,代码将无错误地编译。

没有__declspec(dllexport)赋值后*bd1 = *bd2;,d1::memberB 为 1,但__declspec(dllexport)d1::memberB 保持不变

__declspec(dllexport), 和没有纯虚声明,在赋值后*bd1 = *bd2;d1::memberB 保持不变

4

3 回答 3

7

operator= 不被继承。您的代码在 C++ 中毫无意义,因此编译器可以自由地发出他们想要的任何错误。

从您指向的知识库文章中:http: //support.microsoft.com/kb/130486

由于 operator= 不是继承的,因此基类中的任何 operator= 声明都是未使用且不必要的。不要在基类中声明 operator=。

这可能只是他们编译方式的副作用,他们只是让你知道他们不认为这是一个错误,所以没有必要修复它。“按设计”并不一定意味着他们明确认为这个链接器错误是针对这种情况给出的正确错误消息——代码错误,你得到一个错误,所以从他们的角度来看——他们是完毕。

于 2010-09-21T14:04:35.080 回答
7

从标准的第 12.8 节:

13 为类 X 隐式定义的复制赋值运算符执行其子对象的成员赋值。首先分配 X 的直接基类,按照它们在 base-specifier-list 中的声明顺序,然后按照它们在类定义中声明的顺序分配 X 的直接非静态数据成员. 每个子对象都以适合其类型的方式分配:

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

子类正在使用隐式定义的复制赋值运算符,并且没有定义基类的复制赋值运算符,但是它被声明了,所以你得到的是链接错误而不是编译错误。

于 2010-09-21T14:09:48.610 回答
2

在示例代码中:

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&);,而后者又使用赋值运算符来分配类的基本部分。但是当涉及到链接时,事实证明这从未实现过。

于 2010-09-21T14:09:04.917 回答