C ++在if-else中声明派生类对象并在外部使用它

时间:2016-03-25 10:58:32

标签: c++ inheritance

我有一个名为public void start(Stage primaryStage) { String[] text = {"3", "2", "1"}; Text[] textNodes = new Text[text.length]; for (int i = 0; i < text.length; i++) { Text t = new Text(text[i]); t.setTranslateX(20 + i * 50); t.setTranslateY(100); textNodes[i] = t; } SwapAnimationQueue swapper = new SwapAnimationQueue(textNodes); Pane p = new Pane(); p.getChildren().addAll(textNodes); swapper.swap(0, 1); swapper.swap(1, 2); swapper.swap(0, 1); Scene scene = new Scene(p, 500, 500); primaryStage.setScene(scene); primaryStage.show(); swapper.play(); } 的(父)类,其中包含(虚拟)函数Alma和两个派生类Getwidth(),名为Alma(具有特殊函数{ {1}})和Birs(使用特殊功能Getheight())。我想声明一个名为Citrom的对象 - 哪个类型为Getdepth()Attila,具体取决于Birs。稍后,我想使用公共函数Citrom以及特殊函数(取决于提到的bool)。

我的(不工作)代码:

Getwidth()

6 个答案:

答案 0 :(得分:1)

这是多态对象处理的经典案例。只需确保您熟悉该概念以及指针和参考。

您需要的是:

dynamic_cast

接下来使用{{1}}返回派生类型,最后当然不要忘记删除对象。但首先要阅读这些概念。

答案 1 :(得分:0)

您无法定义类型为this或that的单个对象,具体取决于其他内容。 C ++不能这样工作。 C ++是一种静态类型的语言。这意味着每个对象的类型都是在编译时确定的。其他语言,如Perl或Javascript,是动态类型的,其中对象的类型是在运行时确定的,单个对象在一个点上可以是一样,在不同点上可以是其他东西。

但是C ++不能这样工作。

要执行类似于您尝试执行的操作,您必须重构代码,并使用虚拟超类。像这样:

void UseObject(Alma &andor)
{
  /*Using the common part of object*/  
  std::cout<<andor.Getwidth()<<std::endl;

  /*Using the special part of object*/

  /* This part is your homework assignment */
}


void Useobjects(){

/*Create object depending on bool*/
  if(b00lvar){
    Birs andor;
    std::cout<<Andor.Getwidth()<<" "<<Andor.Getheight()<<std::endl;
    UseObject(andor);
  }else{
    Citrom andor;
    std::cout<<Andor.Getwidth()<<" "<<Andor.Getdepth()<<std::endl;
    UseObject(andor);
  }
}

另一种方法是使用两个指针,在这种情况下,将两个指针传递给UseObject()。其中一个指针始终为nullptr,另一个指向实例化对象的指针,UseObject()编码以处理传入的任何对象。

这也是可能的,但会导致丑陋的代码,如果我是一名教授C ++的讲师,我会记下那些提交代码的人。

答案 2 :(得分:0)

您可以使用指针语义和dynamic_cast类型内省来执行此操作。我扩展了你的例子来展示我将如何接近它。

以下是Demo

#include <iostream>
#include <memory>
using namespace std;

class Alma{
public:
    virtual int Getwidth() = 0;
};

class Birs: public Alma{
public:
    int Getwidth()  { return 1; }
    int Getheight() { return 2; }
};

class Citrom: public Alma{
public:
    int Getwidth() { return 3; }
    int Getdepth() { return 4; }
};

shared_ptr<Alma> make_attila(bool birs)
{
    if (birs)
        return make_shared<Birs>();
    else
        return make_shared<Citrom>();
}

void test_attila(shared_ptr<Alma> attila)
{
    cout << "width: " << attila->Getwidth() << "\n";
    if (auto as_birs = dynamic_pointer_cast<Birs>(attila))
        cout << "height: " << as_birs->Getheight() << "\n";
    else if (auto as_citrom = dynamic_pointer_cast<Citrom>(attila))
        cout << "depth: " << as_citrom->Getdepth() << "\n";
}

int main() {
    shared_ptr<Alma> attila = make_attila(true);
    test_attila(attila);
    attila = make_attila(false);
    test_attila(attila);
    return 0;
}

下一步是使make_attila成为模板函数,将Derived类作为模板参数而不是bool。

template <class Derived>
shared_ptr<Alma> make_attila()
{
   return make_shared<Derived>();
}

答案 3 :(得分:0)

两件事:

  1. 如果您想在if之外使用它, 必须在if之外宣布它。
  2. 需要这种多态性的引用或指针。
  3. unique_ptr<Alma> Andor;
    
    if (b00lvar) {
      Andor = make_unique<Birs>();
    } else {
      Andor = make_unique<Citrom>();
    }
    
    std::cout << Andor->Getwidth() << std::endl;
    

    其他一些答案建议使用shared_ptr,但这样做太过分了。 99%的时间unique_ptr就足够了。

答案 4 :(得分:0)

如果在启动时决定了对象的类型(Alma或Citrom),那么它就是一个经典的多态,正如其他答案所描述的那样: https://stackoverflow.com/a/36218884/185881

您在设计中缺少的是命名具有共同行为的共同祖先(例如Gyumolcs)。

如果对象曾经作为Alma而其他时候作为Citrom,你应该实现一个类,它有一个标志或枚举(ACT_AS_CITROM,ACT_AS_ALMA),或者,如果行为仅限于一个方法,那么它应该有一个参数,告诉你要执行哪个动作(像alma一样或像citrom一样)。

答案 5 :(得分:0)

如果已知某个对象是B或C,那么多态性并不总是可行的。在这种情况下,boost::variant通常更简洁。

话虽如此,如果你想走多态路线,记住引导设计的东西是非常重要的。

多态意味着运行时多态。即程序无法知道对象的真实类型。它也无法知道对象可能存在的全部可能类型,因为另一个开发人员可以制作一个模块代码一无所知的类型。此外,当使用Alma接口时,代码不应该需要知道更多。调用魔法,例如&#34;我知道它将是Citrom因为bool是真的&#34;几个星期或几个月的代码维护噩梦奠定了基础。当在商业,生产代码中完成时,它会导致昂贵且令人尴尬的漏洞狩猎。别这么做。

这证明,Alma界面中必须提供有关Alma类型任何对象的所有相关信息。

在我们的案例中,相关信息是它是否具有高度和/或深度的概念。

在这种情况下,我们应该在基本接口中包含这些属性并提供函数,以便程序在使用之前可以查询属性是否有效。

这就像你用这样写的例子:

#include <iostream>
#include <memory>
#include <typeinfo>
#include <string>
#include <exception>
#include <stdexcept>


// separating out these optional properties will help me to reduce clutter in Alma

struct HeightProperty
{
    bool hasHeight() const { return impl_hasHeight(); }
    int getHeight() const { return impl_getHeight(); }

private:

    // provide default implementations
    virtual bool impl_hasHeight() const { return false; }
    virtual int impl_getHeight() const { throw std::logic_error("getHeight not implemented for this object"); }

};

struct DepthProperty
{
    bool hasDepth() const { return impl_hasDepth(); }
    int getDepth() const { return impl_getDepth(); }

private:

    virtual bool impl_hasDepth() const { return false; }
    virtual int impl_getDepth() const { throw std::logic_error("getDepth not implemented for this object"); }

};

class Alma : public HeightProperty, public DepthProperty
{
public:
    Alma() = default;
    virtual ~Alma() = default;

    // note: nonvirtual interface defers to private virtual implementation
    // this is industry best practice
    int getWidth() const { return impl_getWidth(); }
    const std::string& type() const {
        return impl_getType();
    }
private:
    virtual int impl_getWidth() const = 0;
    virtual const std::string& impl_getType() const = 0;

};

class Birs: public Alma
{
private:
    // implement the mandatory interface
    int impl_getWidth() const override { return 1; }
    const std::string& impl_getType() const override {
        static const std::string type("Birs");
        return type;
    }

    // implement the HeightProperty optional interface
    bool impl_hasHeight() const override { return true; }
    int impl_getHeight() const override { return 2; }
};

class Citrom: public Alma
{
private:
    // implement the mandatory interface
    int impl_getWidth() const override { return 3; }
    const std::string& impl_getType() const override {
        static const std::string type("Citrom");
        return type;
    }

    // implement the DepthProperty optional interface

    bool impl_hasDepth() const override { return true; }
    int impl_getDepth() const override { return 4; }
};

/*...*/
/*Using them*/

// generate either a Birs or a Citrom, but return the Alma interface
std::unique_ptr<Alma> make_alma(bool borc)
{
    if (borc) {
        return std::make_unique<Birs>();
    }
    else {
        return std::make_unique<Citrom>();
    }
}

void Useobjects()
{
    for (bool b : { true, false })
    {
        std::unique_ptr<Alma> pa = make_alma(b);

        std::cout << "this object's typeid name is " << pa->type() << std::endl;
        std::cout << "it's width is : " << pa->getWidth() << std::endl;

        if(pa->hasHeight()) {
            std::cout << "it's height is: " << pa->getHeight() << std::endl;
        }
        if(pa->hasDepth()) {
            std::cout << "it's depth is: " << pa->getDepth() << std::endl;
        }
    }
}

int main()
{
    Useobjects();
    return 0;
}

预期产出:

this object's typeid name is Birs
it's width is : 1
it's height is: 2
this object's typeid name is Citrom
it's width is : 3
it's depth is: 4