C++ - 使派生类从基类"继承"重载赋值运算符的安全/标准方法

Clo*_*oud 3 c++ inheritance overloading class operator-overloading

据我所知,在C++中,派生类不会从基类继承重载的赋值运算符.我在下面写了一个例子,我明确地重载了​​基类和派生类的赋值运算符.在底部的示例输出中,有一个部分:

Index:1 - Base Value:1
Index:1 - Derived Value:2
Index:2 - Base Value:2
Index:2 - Derived Value:2
Run Code Online (Sandbox Code Playgroud)

预期的输出应该是:

Index:1 - Base Value:2
Index:1 - Derived Value:2
Index:2 - Base Value:2
Index:2 - Derived Value:2 
Run Code Online (Sandbox Code Playgroud)

这个输出并不出乎意料.我意识到赋值运算符不是继承的.有一个事实上的安全/标准方法让派生类调用基类的赋值运算符吗?到目前为止,我最好的猜测是让派生类的赋值运算符函数将派生类对象向上转换为基类对象,并将RHS分配给LHS.这是一种普遍接受的安全方法吗?

如果我使用"Proposed Solution"(页面底部),它可以工作,但只是因为没有声明任何运算符virtual.如果我向上转换并使用非虚函数,则使用的函数版本取决于强制转换,而如果我使用虚函数,则使用的成员函数取决于对象的实际类型而不是它的实际类型演员.

谢谢.

代码清单


/*******************************************************************************
 * Preprocessor Directives
 ******************************************************************************/
#include <iostream>
using namespace std;


/*******************************************************************************
 * Class Declarations and Function Prototypes
 ******************************************************************************/
class Base {
   private:
   protected:
   public:
      int iBInt;
      Base();        /* Constructor */
      Base(int a);   /* Constructor - Set const member */
      Base & operator=(const Base& rhs);
      virtual ~Base();
};

class Derived : public Base {
   private:
   protected:
   public:
      int iDInt;
      Derived();        /* Constructor */
      Derived(int a);   /* Constructor - Set const member */
      Derived & operator=(const Derived& rhs);
      ~Derived();
};

/*******************************************************************************
 * Class and Function Definitions
 ******************************************************************************/
/******************************************************************************/
Base::Base(void) {
   cout << __FUNCTION__ << endl;
   iBInt = 0;
   cout << "iBInt: " << iBInt << endl;
}

/******************************************************************************/
Base::Base(int a) {
   cout << __FUNCTION__ << endl;
   iBInt = a;
   cout << "iBInt: " << iBInt << endl;
}

/******************************************************************************/
Base::~Base(void) {
   cout << __FUNCTION__ << endl;
}

/******************************************************************************/
Base& Base::operator=(const Base& rhs) {
   cout << "Base::" << __FUNCTION__ << endl;
   if (this == &rhs) {
      return *this;
   }
   iBInt = rhs.iBInt;
   cout << "iBInt: " << iBInt << endl;
   return *this;
}

/******************************************************************************/
Derived::Derived(void) {
   cout << __FUNCTION__ << endl;
   iDInt = 0;
   cout << "iDInt: " << iDInt << endl;
}

/******************************************************************************/
Derived::Derived(int a) : Base(a) {
   cout << __FUNCTION__ << endl;
   iDInt = a;
   cout << "iDInt: " << iDInt << endl;
}

/******************************************************************************/
Derived::~Derived(void) {
   cout << __FUNCTION__ << endl;
}

/******************************************************************************/
Derived& Derived::operator=(const Derived& rhs) {
   cout << "Derived::" << __FUNCTION__ << endl;
   if (this == &rhs) {
      return *this;
   }
   iDInt = rhs.iDInt;
   cout << "iDInt: " << iDInt << endl;
   return *this;
}


/*******************************************************************************
 * Main Entry Point
 ******************************************************************************/
int main(void) {
   int count = 3;
   /* Generate objects */
   Derived **bArr = new Derived*[count];
   for (int i=0; i<count; i++) {
      bArr[i] = new Derived(i);
   }

   /* Set some values via overloaded assignment operator, and print out
    * updated values.
    */
   for (int i=0; i<count; i++) {
      cout << "Index:" << i << " - Base Value:" << bArr[i]->iBInt << endl;
      cout << "Index:" << i << " - Derived Value:" << bArr[i]->iDInt << endl;
   }
   *bArr[1] = *bArr[2];
   for (int i=0; i<count; i++) {
      cout << "Index:" << i << " - Base Value:" << bArr[i]->iBInt << endl;
      cout << "Index:" << i << " - Derived Value:" << bArr[i]->iDInt << endl;
   }
   /* Cleanup */
   for (int i=0; i<count; i++) {
      delete bArr[i];
   }
   delete [] bArr;

   return 0;
}
Run Code Online (Sandbox Code Playgroud)

样本输出


Base
iBInt: 0
Derived
iDInt: 0
Base
iBInt: 1
Derived
iDInt: 1
Base
iBInt: 2
Derived
iDInt: 2
Index:0 - Base Value:0
Index:0 - Derived Value:0
Index:1 - Base Value:1
Index:1 - Derived Value:1
Index:2 - Base Value:2
Index:2 - Derived Value:2
Derived::operator=
iDInt: 2
Index:0 - Base Value:0
Index:0 - Derived Value:0
Index:1 - Base Value:1
Index:1 - Derived Value:2
Index:2 - Base Value:2
Index:2 - Derived Value:2
~Derived
~Base
~Derived
~Base
~Derived
~Base
Run Code Online (Sandbox Code Playgroud)

建议的解决方案/编辑


/******************************************************************************/
Derived& Derived::operator=(const Derived& rhs) {
   cout << "Derived::" << __FUNCTION__ << endl;
   if (this == &rhs) {
      return *this;
   }
   Base* b1 = this;
   Base* b2 = (Base*)&rhs;
   *b1 = *b2;
   iDInt = rhs.iDInt;
   cout << "iDInt: " << iDInt << endl;
   return *this;
}
Run Code Online (Sandbox Code Playgroud)

cdh*_*wie 7

我想直接调用Base赋值运算符:

Base::operator =(rhs);
Run Code Online (Sandbox Code Playgroud)

以这种方式调用继承的类赋值运算符比使用指针体操调用它更干净和直接.


另一种方法是:

static_cast<Base &>(*this) = rhs;
Run Code Online (Sandbox Code Playgroud)

这比使用指针调用运算符更干净,但仍然(IMO)比显式调用基本运算符重载更不易读.

  • @Dogbert这种技术不需要复制构造函数; 它显式调用基类的赋值运算符.如果这不是你的问题,那么我不确定你在问什么. (2认同)
  • @Dogbert遵循[三条规则](http://en.wikipedia.org/wiki/Rule_of_three_(C%2B%2B_programming)) - 如果您需要定义自定义析构函数,复制构造函数或赋值运算符,那么您需要定义所有三个.通常,如果编译器生成的默认实现可行,则不应定义*any*.(在您给出的示例代码中,它们可以正常工作,但我确实知道这是一个人为的练习.) (2认同)
  • `派生d1 =派生(a)`永远不会使用赋值运算符,它将始终使用复制构造函数.(如果删除了复制构造函数,则该行将无法编译:`error:使用已删除的函数'Derived :: Derived(const Derived&)'`.即使您提供无参数构造函数和赋值,也是如此运营商! (2认同)
  • 我应该注意到,大多数编译器都会在'Derived d1 = Derived(a)`中删除副本.然而,它们仍然需要一个可见的复制构造函数,因为elision是一种优化 - 您不会违反规范,因为您可以访问优化编译器. (2认同)