虚拟赋值运算符C++; C++中赋值操作符可以虚拟化。为什么需要它?我们可以让其他操作符也虚拟吗?

虚拟赋值运算符C++; C++中赋值操作符可以虚拟化。为什么需要它?我们可以让其他操作符也虚拟吗?,c++,operator-overloading,virtual,virtual-functions,C++,Operator Overloading,Virtual,Virtual Functions,只有当您希望保证从类派生的类正确复制其所有成员时,才需要虚拟操作符。如果您没有使用多态性做任何事情,那么您就不必担心这一点 我不知道有什么会阻止你虚拟化你想要的任何操作符——它们只是特例方法调用 提供了所有这些工作原理的详细说明。这取决于操作员 将赋值运算符设置为虚拟运算符的目的是使您能够从能够覆盖它以复制更多字段的好处中获益 因此,如果您有一个基本类型&并且您实际上有一个派生类型&作为一个动态类型,并且派生类型有更多的字段,那么将复制正确的内容 但是,存在这样的风险,即您的LHS是派生的,而R

只有当您希望保证从类派生的类正确复制其所有成员时,才需要虚拟操作符。如果您没有使用多态性做任何事情,那么您就不必担心这一点

我不知道有什么会阻止你虚拟化你想要的任何操作符——它们只是特例方法调用


提供了所有这些工作原理的详细说明。

这取决于操作员

将赋值运算符设置为虚拟运算符的目的是使您能够从能够覆盖它以复制更多字段的好处中获益

因此,如果您有一个基本类型&并且您实际上有一个派生类型&作为一个动态类型,并且派生类型有更多的字段,那么将复制正确的内容

但是,存在这样的风险,即您的LHS是派生的,而RHS是基础,因此,当虚拟操作符在派生中运行时,您的参数不是派生的,您无法从中获取字段

下面是一个很好的讨论:

不需要将赋值运算符设置为虚拟运算符

下面的讨论是关于
operator=
,但它也适用于接受相关类型的任何运算符重载,以及接受相关类型的任何函数

下面的讨论表明,virtual关键字不知道有关查找匹配函数签名的参数继承。在最后一个示例中,它展示了在处理继承类型时如何正确处理赋值


虚拟函数不知道参数的继承:

//code snippet
Class Base;
Class Child :public Base;

Child obj1 , obj2;
Base *ptr1 , *ptr2;

ptr1= &obj1;
ptr2= &obj2 ;

//Virtual Function prototypes:
Base& operator=(const Base& obj);
Child& operator=(const Child& obj);
一个函数的签名必须是相同的,才能让virtual发挥作用。因此,即使在下面的示例中,operator=是虚拟的,调用也永远不会作为D中的虚拟函数,因为operator=的参数和返回值是不同的

函数
B::operator=(常数B&right)
D::operator=(常数D&right)
完全不同,被视为两个不同的函数

class B
{
public:
  virtual B& operator=(const B& right)
  {
    x = right.x;
    return *this;
  }

  int x;

};

class D : public B
{
public:
  virtual D& operator=(const D& right)
  {
    x = right.x;
    y = right.y;
    return *this;
  }
  int y;
};

默认值并有2个重载运算符:

//code snippet
Class Base;
Class Child :public Base;

Child obj1 , obj2;
Base *ptr1 , *ptr2;

ptr1= &obj1;
ptr2= &obj2 ;

//Virtual Function prototypes:
Base& operator=(const Base& obj);
Child& operator=(const Child& obj);
不过,您可以定义一个虚拟函数,以允许您在将D分配给类型B的变量时为D设置默认值。即使您的B变量实际上是存储在a B引用中的a D,也可以这样做。您将无法获得
D::operator=(const D&right)
函数

在下面的例子中,来自存储在2 B引用中的2 D对象的赋值。。。使用
D::operator=(常量B&right)
覆盖

//Use same B as above

class D : public B
{
public:
  virtual D& operator=(const D& right)
  {
    x = right.x;
    y = right.y;
    return *this;
  }


  virtual B& operator=(const B& right)
  {
    x = right.x;
    y = 13;//Default value
    return *this;
  }

  int y;
};


int main(int argc, char **argv) 
{
  D d1;
  B &b1 = d1;
  d1.x = 99;
  d1.y = 100;
  printf("d1.x d1.y %i %i\n", d1.x, d1.y);

  D d2;
  B &b2 = d2;
  b2 = b1;
  printf("d2.x d2.y %i %i\n", d2.x, d2.y);
  return 0;
}
印刷品:

d1.x d1.y 99 100
d2.x d2.y 99 13
这表明从未使用过
D::operator=(const D&right)

如果没有
B::operator=(const B&right)
上的virtual关键字,您将得到与上面相同的结果,但是y的值不会被初始化。也就是说,它将使用
B::operator=(const B&right)


最后一步是将所有这些联系在一起,RTTI:

//code snippet
Class Base;
Class Child :public Base;

Child obj1 , obj2;
Base *ptr1 , *ptr2;

ptr1= &obj1;
ptr2= &obj2 ;

//Virtual Function prototypes:
Base& operator=(const Base& obj);
Child& operator=(const Child& obj);
您可以使用RTTI正确处理接受您的类型的虚拟函数。这里是拼图的最后一个部分,以了解在处理可能继承的类型时如何正确处理赋值

virtual B& operator=(const B& right)
{
  const D *pD = dynamic_cast<const D*>(&right);
  if(pD)
  {
    x = pD->x;
    y = pD->y;
  }
  else
  {
    x = right.x;
    y = 13;//default value
  }

  return *this;
}
virtual B& operator=(const B& right)
{
  const D *pD = dynamic_cast<const D*>(&right);
  if(pD)
  {
    x = pD->x;
    y = pD->y;
  }
  else
  {
    x = right.x;
    y = 13;//default value
  }

  return *this;
}
virtualb&operator=(常量B&right)
{
const D*pD=动态施法(&R);
如果(pD)
{
x=pD->x;
y=pD->y;
}
其他的
{
x=右。x;
y=13;//默认值
}
归还*这个;
}
写的:
最后一步是将所有这些联系在一起,RTTI:

//code snippet
Class Base;
Class Child :public Base;

Child obj1 , obj2;
Base *ptr1 , *ptr2;

ptr1= &obj1;
ptr2= &obj2 ;

//Virtual Function prototypes:
Base& operator=(const Base& obj);
Child& operator=(const Child& obj);
您可以使用RTTI正确处理接受您的类型的虚拟函数。这里是拼图的最后一个部分,以了解在处理可能继承的类型时如何正确处理赋值

virtual B& operator=(const B& right)
{
  const D *pD = dynamic_cast<const D*>(&right);
  if(pD)
  {
    x = pD->x;
    y = pD->y;
  }
  else
  {
    x = right.x;
    y = 13;//default value
  }

  return *this;
}
virtual B& operator=(const B& right)
{
  const D *pD = dynamic_cast<const D*>(&right);
  if(pD)
  {
    x = pD->x;
    y = pD->y;
  }
  else
  {
    x = right.x;
    y = 13;//default value
  }

  return *this;
}
以下代码完成了我引用的RTTI解决方案:

class B
{
public:
    virtual B& operator=(const B& right)
    {
        x = right.x;
        return *this;
    }

    int x;
};
class D : public B{
public:
    // The virtual keyword is optional here because this
    // method has already been declared virtual in B class
    /* virtual */ const D& operator =(const B& b){
        // Copy fields for base class
        B::operator =(b);
        try{
            const D& d = dynamic_cast<const D&>(b);
            // Copy D fields
            y = d.y;
        }
        catch (std::bad_cast){
            // Set default values or do nothing
        }
        return *this;
    }

    // Overload the assignment operator
    // It is required to have the virtual keyword because
    // you are defining a new method. Even if other methods
    // with the same name are declared virtual it doesn't
    // make this one virtual.
    virtual const D& operator =(const D& d){
        // Copy fields from B
        B::operator =(d);
        // Copy D fields
        y = d.y;
        return *this;
    }

    int y;
};
D类:公共B类{
公众:
//此处的virtual关键字是可选的,因为
//方法已在B类中声明为虚拟
/*虚拟*/常量D和运算符=(常量B和B){
//复制基类的字段
B::运算符=(B);
试一试{
常数D&D=动态投影(b);
//复制D字段
y=d.y;
}
捕捉(标准::错误的投射){
//设置默认值或不执行任何操作
}
归还*这个;
}
//重载赋值运算符
//需要有virtual关键字,因为
//您正在定义一个新方法。即使其他方法
//具有相同名称的被声明为虚拟,但不
//让这个虚拟的。
虚拟常量D和运算符=(常量D和D){
//从B复制字段
B::运算符=(d);
//复制D字段
y=d.y;
归还*这个;
}
int-y;
};
这似乎是一个完整的解决方案,但事实并非如此。这不是一个完整的解决方案,因为当从D派生时,需要1个运算符=取常数B&,1个运算符=取常数D&,一个运算符=取常数D2&。结论很明显,运算符=()重载的数量与超类的数量+1相等

考虑d2继承d,让我们来看看这两个继承的运算符=()方法是什么样子的。

class D2 : public D{
    /* virtual */ const D2& operator =(const B& b){
        D::operator =(b); // Maybe it's a D instance referenced by a B reference.
        try{
            const D2& d2 = dynamic_cast<const D2&>(b);
            // Copy D2 stuff
        }
        catch (std::bad_cast){
            // Set defaults or do nothing
        }
        return *this;
    }

    /* virtual */ const D2& operator =(const D& d){
        D::operator =(d);
        try{
            const D2& d2 = dynamic_cast<const D2&>(d);
            // Copy D2 stuff
        }
        catch (std::bad_cast){
            // Set defaults or do nothing
        }
        return *this;
    }
};
D2类:公共D{ /*虚拟*/常量D2和运算符=(常量B和B){ D::operator=(b);//可能是b引用引用的D实例。 试一试{ 常数D2和D2=动态投影(b); //复制D2材料 } 捕捉(标准::错误的投射){ //设置默认值或不执行任何操作 } 归还*这个; } /*虚拟*/常数D2和运算符=(常数D和D){ D::运算符=(D); 试一试{ 常数D2和D2=动态投影(d); //复制D2材料 } 捕捉(标准::错误的投射){