【问题标题】:How to cast pointer to base type conditionally given a pointer to derived type如何在给定派生类型的指针的情况下有条件地将指针转换为基类型
【发布时间】:2014-12-25 14:57:27
【问题描述】:

我有两个派生类,TriangleSphere,来自基类 Shape

我有一种情况,我在类 A 中存储了一个基类指针 Shape*,并且对于每个派生类型 doSomething(Sphere* sp)doSomething(Triangle* tr) 都有两个类 A 的重载成员函数。

class Shape
{
public:
    Shape() {};
    virtual ~Shape() = 0;
};

class Triangle : public Shape
{
public:
    Triangle();
};

class Sphere : public Shape
{
public:
    Sphere();
};

class A
{
private:
    Shape* shape;
    void doSomething(Triangle* tr);
    void doSomething(Sphere* sp);
public:
    A();
    ~A();
};

但是,当我尝试通过将 Shape* 类型的指针传递给 doSomething 函数来运行我的代码时,我收到以下错误:

candidate function not viable: cannot convert from base class pointer 'value_type'
      (aka 'Shape *') to derived class pointer 'const Triangle *' for 1st argument
void A::doSomething(Triangle* tr) {

Sphere 的消息相同。我也确信虽然Shape * 类型的指针,它实际上指向派生对象之一。

那么,在不修改 TriangleSphere 类且只修改 A 的情况下,我该如何解决这个问题?

【问题讨论】:

  • 无论人们是否有能力阅读字里行间并推断出您所询问的内容,如果您包含代码总是会更有帮助。考虑编辑以提供"Minimal, Complete, Verifiable Example"
  • 重载解析纯粹是编译时的事情。如果动态类型与静态类型不同,您可以在运行时使用dynamic_cast typeidvirtual 函数的不同行为进行测试。前提是静态类型(即基类类型)至少有一个virtual函数或基类。

标签: c++ pointers casting


【解决方案1】:

必须有一些决策过程才能在球体或三角形之间进行选择。

如果您在编译时决定想要什么形状,则可以保留您的设计,并使用部分模板专业化。 (本示例已使用 g++ 4.7.2 编译)

#include <iostream>

using std::cout;
using std::endl;

struct Shape
{
};

struct Sphere : Shape
{
    double radius;
    Sphere(double radius): radius(radius) {}
};

struct Triangle : Shape
{
    double a, b, c;
    Triangle(double a, double b, double c): a(a), b(b), c(c) {}
};

void foo(Sphere * b)
{
    cout << "Sphere " << b->radius << endl;
};

void foo(Triangle * c)
{
    cout << "Triangle " << c->a << ',' << c->b << ',' << c->c << endl;
};

template<bool>
struct ShapeShifter
{
};

template<>
struct ShapeShifter<true>
{
    typedef Sphere* Type;
};

template<>
struct ShapeShifter<false>
{
    typedef Triangle* Type;
};

int main()
{
    Shape * a1 = new Sphere(1.5);
    Shape * a2 = new Triangle(3,4,5);
    const int x = 0;

    foo(static_cast<ShapeShifter<x == 0>::Type>(a1));
    foo(static_cast<ShapeShifter<x != 0>::Type>(a2));
}

如前所述,否则您可以使用dynamic_cast

【讨论】:

  • 这种方法容易出错。我在函数调用中更改了 a1 和 a2,程序仍然编译并打印了值。
  • 所以您只是在没有任何运行时信息的情况下随意将Shape 转换为TriangleSphere?是的,我认为这很容易出错!
【解决方案2】:

您需要使用dynamic_cast 找出Shape* 指向的派生类并获取指向该类的指针,然后您可以根据需要将该指针传递给doSomething() 重载,例如:

class A
{
private:
    Shape* shape;
    void doSomething(Shape* sh);
    void doSomething(Triangle* tr);
    void doSomething(Sphere* sp);
public:
    A();
    ~A();
};

void A::doSomething(Shape* sh)
{
    if (Sphere *sp = dynamic_cast<Sphere*>(sh))
        doSomething(sp);

    else if (Triangle *tr = dynamic_cast<Triangle*>(sh))
        doSomething(tr);
}

【讨论】:

  • 请注意,这通常被认为是不好的做法,并且更倾向于使用虚函数。
  • 我同意,但问题的要求不允许这种可能性。
【解决方案3】:

在 C++ 中无法做到这一点。进行动态调度的唯一方法是调用虚拟方法:

shape->doSomething();

Triangle::doSomethingSphere::doSomething 可能是虚拟Shape::doSomething 的不同实现。

因此,要么您需要更改代码结构,以便由 shape 本身控制调用。或者...您可以拥有一个庞大的分支,依次尝试每个分支:

void doSomething() {
    if (Triangle* t = dynamic_cast<Triangle*>(shape)) {
        doSomething(t);
    }
    else if (Sphere* s = dynamic_cast<Sphere*>(shape)) {
        doSomething(s);
    }
    // etc.
}

我建议不要这样做。除了阅读起来很糟糕之外,它还很慢且无法维护。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-09-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-02-02
    • 2021-02-09
    • 2016-10-10
    • 2012-03-22
    相关资源
    最近更新 更多