Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/134.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 从基类方法返回对派生类的引用_C++_Inheritance - Fatal编程技术网

C++ 从基类方法返回对派生类的引用

C++ 从基类方法返回对派生类的引用,c++,inheritance,C++,Inheritance,我的任务是实现一个简单的SVG生成器。我需要支持圆,多段线和文字。这三种方法至少有4种常用方法: -设定行程颜色 -SetFillColor -设定行程宽度 -托斯特林 主要要求之一是支持链接,例如: 多段线{}.SetStrokeColor(“白色”).SetFillColor(“黑色”) 我决定实现一个基类元素,所有其他类都从该元素继承。其思想是创建一个类文档,其中包含添加到文档中的所有元素的向量。 基本方法的示例签名: //故障源 元素和设置行程颜色(常量颜色和颜色){ ... 归还*这个

我的任务是实现一个简单的SVG生成器。我需要支持圆,多段线和文字。这三种方法至少有4种常用方法: -设定行程颜色 -SetFillColor -设定行程宽度 -托斯特林 主要要求之一是支持链接,例如: 多段线{}.SetStrokeColor(“白色”).SetFillColor(“黑色”)

我决定实现一个基类元素,所有其他类都从该元素继承。其思想是创建一个类文档,其中包含添加到文档中的所有元素的向量。 基本方法的示例签名:

//故障源
元素和设置行程颜色(常量颜色和颜色){
...
归还*这个;
}
我的派生类确实调用这些方法,但问题是这些方法返回对基类元素的引用,而不是派生类

我的问题是C++中是否可以一起实现???
对于协变返回类型,您可以

class Element {
  public:
    // ...

    virtual Element& refToThis() { return *this; };
};
在派生类中

class Derived : public Element {
  public:
    // ...

    Derived& refToThis() override { return *this; };
};
当静态类型为
派生
(例如,内部
派生
本身)时,可将
派生
实例处理为
派生
实例。当静态类型为
Element
时,
refToThis()
的返回类型也为。

用于比较:

class Base {};
class Derived : public Base {};

Derived d;
Base* p = &d; // points to a Base that in reality is a derived
Base& b =  d; // in this respect, references do not differ...

// and you can get the original type back:
auto dr = static_cast<Derived&>(b);  // if you know 100% for sure that b is a Derived
auto dp = dynamic_cast<Derived*>(p); // if p might point to another type; you get
                                     // a null pointer back, if it does
对于这种方法,来自
元素
类的原始函数需要是非虚拟的(实际上是一种优势,因为虚拟函数调用的成本更高……);在模板中,参数(
T
,即
Circle
)尚未完全定义(使用CRTP模式),编译器将无法检查返回类型是否真的是共变量。所以实际上,我们只是在隐藏基类的函数

与重写相比,这仍然没有缺点:仅当直接对对象调用(虚拟)函数时,协变量返回类型才可用:

Circle c;
c.setStrokeWidth(7).setCenter();

Element& e = c;
e.setStrokeWidth(7).setCenter(); // fails, even if you implement co-variant override directly

如果您希望共享实现并保留类型信息,则CRTP是您所需要的:

struct ElementBase { };

template <class Concrete>
struct Element : ElementBase {

    Concrete &setStrokeWidth(int width) {
        // Actual implementation...
        (void) width;

        return cthis();
    }

private:
    friend Concrete;
    Element() = default;

    Concrete &cthis() { return static_cast<Concrete &>(*this); }
    Concrete &cthis() const { return static_cast<Concrete const &>(*this); }
};

struct Circle : Element<Circle> {
    Circle &SetCircleCenter(int x, int y) {
        // Actual implementation...
        (void) x;
        (void) y;

        return *this;
    }
};

int main() {
    Circle c;
    c.setStrokeWidth(4).SetCircleCenter(0, 0);
}
struct ElementBase{};
模板
结构元素:ElementBase{
混凝土和调整行程宽度(内部宽度){
//实际执行。。。
(空隙)宽度;
返回cthis();
}
私人:
朋友混凝土;
元素()=默认值;
Concrete&cthis(){return static_cast(*this);}
const{return static_cast(*this);}
};
结构圆:元素{
圆和设置圆中心(整数x,整数y){
//实际执行。。。
(无效)x;
(无效)y;
归还*这个;
}
};
int main(){
圈c;
c、 设置行程宽度(4)。设置循环中心(0,0);
}

请花些时间刷新、重做和重读,以及。最后,请不要忘记如何创建一个。这根本不是问题-仅仅是您的签名有一个多余的符号和…@Aconcagua typoua对基的引用是对派生类的抽象引用。你到底想要实现什么?假设您希望将所有元素存储在一个向量中,那么您以后只能访问
元素
,当然可以;请注意,它仍然产生了有效的C++,并且您将返回一个rValk引用…谢谢答案!我还是不明白。基本方法不仅仅返回一个ref。它还执行一些逻辑等操作。这是否意味着我必须为每个派生类重新实现该方法?是的,如果您希望通过
SomeDerivedClass::refToThis()
获得派生类型,那么每个子类都必须实现该成员函数。唉,这不起作用,因为在声明
setStrokeWidth
时,
Circle
(aka
T
)不完整,其继承图未知。因此,返回类型的协方差无法验证,代码也无法编译。仍然没有骰子(我在写答案时偶然发现了它…)——即使没有显式的
覆盖
签名匹配,也无法执行检查。我认为这只能通过为函数使用另一个名称(或签名)来解决,即a la NVI。这里有一个。编辑:等等,你的代码也可以在叮当声上工作。我做错了什么…明白了:您的基类函数不是
virtual
。我还没疯呢,耶!只需将
virtual
添加到演示中的
Element::setStrokeWidth
,您就会看到:)缺少一点:如何将Element类型的对象放入向量?找到一个答案:派生元素来自非模板基类。使其成为唯一的。您是否介意将其添加到您的解决方案中,以便其他人不必阅读注释?@magom001当然可以,但请注意,由于lubgr的回答中描述的问题,您将无法从基类动态调用函数。当然,在我的特殊情况下,我只需要调用一个ToString方法,我在基类中声明它为纯虚,在子类中重写它。这就是链接的目的:调用Svg::Document::Add(Svg::Circle{}.SetCenter({0,0}).SetRadius(2)),Add方法执行vector.emplace_back(make_unique(Circle)),当我需要生成一个Svg时,我只需迭代vector中的所有元素,并在每个元素上调用ToString。
struct ElementBase { };

template <class Concrete>
struct Element : ElementBase {

    Concrete &setStrokeWidth(int width) {
        // Actual implementation...
        (void) width;

        return cthis();
    }

private:
    friend Concrete;
    Element() = default;

    Concrete &cthis() { return static_cast<Concrete &>(*this); }
    Concrete &cthis() const { return static_cast<Concrete const &>(*this); }
};

struct Circle : Element<Circle> {
    Circle &SetCircleCenter(int x, int y) {
        // Actual implementation...
        (void) x;
        (void) y;

        return *this;
    }
};

int main() {
    Circle c;
    c.setStrokeWidth(4).SetCircleCenter(0, 0);
}