c++ - 在以抽象基类为参数的函数中将指针和赋值运算符与派生类一起使用

标签 c++ pointers inheritance abstract-class assignment-operator

我有一个函数 (modShape),它接受一个抽象基类 (Shape) 作为参数;在函数中,我想制作输入对象的拷贝,修改拷贝,然后将拷贝重新分配给输入对象,以便修改保留在 modShape 的范围之上。

我已经设置了一个 clone() 成员函数来制作初始拷贝,它似乎运行良好。接下来,我使用 doubleArea() 成员函数修改拷贝,并尝试将其复制回输入对象。

基类和派生类在header.h中定义:

#ifndef HEADER_H_
#define HEADER_H_

#include <iostream>
#include <cmath>

using namespace std;

// Abstract base class.
class Shape {
 public:
  // Virtual functions
  virtual double area() { return 0; }
  virtual double perimeter() { return 0; }
  virtual void doubleArea() { /* do nothing */ }
  virtual Shape* clone() const = 0;

};

// Derived class.
class Circle: public Shape {
 private:
  double radius;

 public:
  Circle (double r) : radius(r) {}
  double area() { return (M_PI*pow(radius,2)); }
  double perimeter() { return (M_PI*2*radius); }
  void doubleArea() { radius *= pow(2,0.5); }
  Circle* clone() const { return new Circle(*this); }

};

#endif

函数modShape和测试代码在main.cpp中:

#include <iostream>
#include "header.h"

using namespace std;

void modShape(Shape &inShape) {

  // Make new Shape* from clone of inShape
  // and double its area.
  Shape* newShape = inShape.clone();
  newShape->doubleArea();
  cout << "newShape's area (after doubling): " << newShape->area() << endl;

  // Copy newShape to inShape.
  inShape = *newShape;
  cout << "newShape copied to inShape (circ)." << endl;
  cout << "inShape's area in modShape: " << inShape.area() << endl;

};


int main() {

  Circle circ(2);
  cout << "circ's initial area (in main): " << circ.area() << endl;
  modShape(circ);
  cout << "circ's final area (in main): " << circ.area() << endl;

  return 0;
}

我从这个函数得到的输出是:

circ's initial area (in main): 12.5664

newShape's area (after doubling): 25.1327

newShape copied to inShape.

inShape's area in modShape(): 12.5664

circ's final area (in main): 12.5664

很明显,赋值 inShape = *newShape 没有像我预期的那样工作。我的猜测是正在使用的赋值运算符是针对 Shape 类的,因此不会从派生类(如 radius)复制成员变量?如果是这种情况,我想我想定义一个赋值运算符,它将“知道”对象是派生类,即使它们被定义为基类,但我不确定该怎么做。或者如果有更好的解决方案,请告诉我!非常感谢任何建议。

更新: 看起来切片是问题所在,现在我需要弄清楚如何避免它。我想如果我定义我的函数来接受一个指针,事情会更好:

void modShape2(Shape* inShape) {
  Shape* newShape = inShape->clone();
  cout << inShape->area() << endl;
  inShape = newShape;
  cout << inShape->area() << endl;
}

我设置为:

Circle *circ2 = new Circle(1);
cout << "circ2's initial area (in main): " << circ2->area() << endl;
modShape2(circ2);
cout << "circ2's final area (in main): " << circ2->area() << endl;

这里产生的输出是

circ2's final area (in main): 3.14159

3.14159

6.28319

circ2's final area (in main): 3.14159

在这种情况下,似乎复制是在没有切片的情况下发生的,因为该区域在 modShape2 函数内部被加倍了,但是当我们出于某种原因超出 modShape2 的范围时,更改没有进行。我对此感到非常困惑!

最佳答案

问题

你已经很好地识别了它。该错误是由以下语句引起的,它导致object slicing :

inShape = *newShape;

因此仅复制 Shape 基础对象中的成员。不属于基类的区域不会被复制。

如何做鞋底?

定义一个虚拟赋值运算符是不可取的,因为这个运算符对于类 T 的通常签名是:

 T& operator= (const T& r);   

这样你就会在返回类型上遇到麻烦。

一个更简单的解决方案,将有一个虚拟复制功能(与克隆功能相似的原理):

class Shape {
    ...
    virtual void copy(const Shape&r) = 0; 
};

它将为派生对象实现,检查类型是否匹配并使用类型的赋值运算符。例如:

void copy(const Shape&r) override {
    if (dynamic_cast<const Circle*>(&r)) 
        *this = *dynamic_cast<const Circle*>(&r);
    else throw (invalid_argument("ouch! circle copy mismatch"));
}

这里an online demo .

关于c++ - 在以抽象基类为参数的函数中将指针和赋值运算符与派生类一起使用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/35566202/

相关文章:

c - 在 C 中声明全局多维数组

c - 返回 char * 值

c# - 多态基础

c++ - 如何将用户输入(字符串)转换为字符数组?

c++ - 调用C++中隐式转换可到达的成员函数

c++ - 强制调用 "highest"重载函数而不是基函数

c++ - 是否可以从 C++ 中的基类方法返回派生类?

C++ `typedef Ptr<Layer>(*Constructor)(LayerParams &params);`

c - 如何将 3D 字符数组传递给函数

java - 哪种设计模式用于创建继承对象?