繁体   English   中英

C ++继承函数覆盖

[英]C++ inheritance function override

我是C ++的新手,我来自Java / C#。

我知道在Java和C#中你可以创建一个类,让另一个类继承它,并覆盖它的功能。 然后,您可以创建父类的列表,并将子类的对象插入此列表中。 之后,您可以使用被覆盖的功能。

例:

public class Parent
{
  public virtual void test()
  {
    Console.WriteLine("test");
  }
}

public class Child : Parent
{
  public override void test()
  {
    Console.WriteLine("test2");
  }
}

用法:

List<Parent> tests = new List<Parent>();
tests.Add(new Child());
tests[0].test();

输出:

 test2 

在C ++中, 当我使用std::vector执行此操作时 ,它调用父代的成员函数,而不是子函数。

如何在C ++中完成上述操作?

我觉得这里有两个问题。 一个是语法问题,其他人已经解决了。 但是,您似乎也有尝试用C ++编写Java / C#代码的根本问题。 无论语法问题是什么,这都会导致痛苦,所以我试着在这里解决这个问题。

在c ++中,当我使用vector执行此操作时,它会调用父项的函数。 我怎样才能在C ++中做上面的例子?

Java和C#使用面向对象的范例来实现一切 C ++的不同之处在于C ++是一种多范式语言 它支持(或多或少)结构化,面向对象,通用,功能和诸如编程范例。 你可以自由地混合和混合范式 ,而C ++在你做到这一点的时候会闪亮一点。

源自STL的标准库的一部分,即:容器,算法,迭代器,根本不是OO。 他们正在应用通用编程 其中一个属性是容器通常(有异常,但不在标准库本身内)存储 ,而不是引用 然而,多态性,至少是运行时多态性,仅对引用 (或语法上的指针,也是语义上的引用)进行操作。

如果你有一个std::vector<base_class> vc ,这将存储实际 ,而不是对堆上某处对象的引用。 如果将对象放入这样的容器中,该对象实际上将被复制到容器中。 如果你输入一个derived_class对象,那么它将被切片 也就是说,只有它的base_class部分将被复制到容器中,所有的derived_class部分都将被忽略。 然后,您最终在容器中得到一个实际的base_class对象,而不是像在Java和C#中那样,是对堆上某个派生类对象的基类引用。
这就是为什么在该对象上调用成员函数将最终在基类中: 没有派生类对象来调用函数

在C ++中, 如果要使用OOP ,通常必须动态分配派生类对象 (即new derived_class() )并将它们分配给基类指针。 这个问题是C ++没有垃圾收集,所以你必须跟踪那些指针,以及从中生成的所有副本,并在最后一个指针被销毁之前显式删除对象。 手动操作非常容易出错 ,这就是为什么现在每个人都让智能指针自动执行此操作。

所以你想要的是std::vector<smart_ptr<base_class>>并放入new derived_class()对象。 符号smart_ptr所指的内容取决于您的需求。 如果你计划在那个容器中存储指向那些对象的指针,那么std::unique_ptrstd::tr1::unique_ptr如果你的编译器只支持C ++ 03,或者boost::unique_ptr如果它甚至不支持那个)会是理想的。 如果你自由地传递这样的指针,并且它们跟踪最后一次超出自己的范围, std::shared_ptr会更好。


现在,所有这些说,我觉得有必要补充: 你可能根本不需要OO方式 如果你可以放弃刚刚认真考虑Java和C#监禁你的OO,可能会有更好的设计。

如果您使用多态,那么您可以将具有不同内容的容器传递给相同的算法,那么使用通用编程可能会好得多

template<typename FwdIt>
void do_something(FwdIt begin, FwdIt end)
{
  while(begin != end)
    if(begin->foo() == bar()) // whatever
      begin->baz();           // whatever
}

std::vector<some_class> vs;
std::vector<other_class> vo;
std::deque<other_class> do;

// ...

do_something(vs.begin(), vs.end());
do_something(vo.begin(), vo.end());
do_something(do.begin(), do.end());

这适用于所有类型(这里是some_class ),其中foo()成员不接受任何参数并返回与bar()返回的内容相当的东西,并且具有baz()成员,也不接受任何参数。 (如果你尝试使用某些没有那些类型的类型,编译器会咆哮你。)

与Java或C#不同,C ++默认使用值语义。 std::vector<Parent>包含Parent类型的实际对象,而不包含指针或引用。 插入向量时,将复制要插入的对象,并将其复制到Parent类型的对象中。 (对象不能更改类型。)这称为切片。

如果要在C ++中使用多态,则必须明确指定要引用语义。 指针和引用都提供了引用语义,并且可以定义“智能指针” - 类似于指向其他类的指针的类。 由于引用不支持标准容器所需的复制/赋值语义,因此它们不能用于实例化容器,因此如果容器要容纳多态对象,则必须将其定义为包含指针。 所以:

std::vector<ValueType> v;
v.push_back( ValueType() );         //  no new

std::vector<BaseType*> v;
v.push_back( new DerivedType() );   //  dynamic allocation.

由于切片,多态性和复制/赋值不能很好地协同工作,并且通常在设计为层次结构基础的类中阻止复制/赋值。

此外,如果要通过指向基类的指针来管理对象,则析构函数应该是虚拟的:

class Parent
{
public:
    virtual ~Parent() {}
    //  ...
};

否则,当您删除对象时(通过指向其基础的指针),您将遇到未定义的行为。

test()应该在您的Parent类中变为virtual ,以确保调用Child类的test()

看起来boost::ptr_container库对你非常有帮助。 它的工作方式与(智能)指针的向量相同,但它具有设计用于此类的语法的额外好处。

例如,您可以执行以下操作:

typedef boost::ptr_vector<AbstractClass> PolyVector;

PolyVector polyVect;
polyVect.push_back( std::unique_ptr( new ChildClassA() ) );
polyVect.push_back( std::unique_ptr( new ChildClassB() ) );
polyVect.push_back( std::unique_ptr( new ChildClassC() ) );

BOOST_FOREACH( PolyVector::value_type item, polyVect)
    item.memberFunction( x );

这将调用virtual memberFunction的派生类实现。

C ++没有override关键字。 只需将重写的方法重新声明为virtual方法。

在C ++中,这看起来如下所示:

MyList<Parent*>* tests = new MyList<Parent*>();
tests->Add(new Child());
tests->test();

为了在C ++中调用多态函数,您将调用子函数而不是父函数,您必须使用指向或引用父类的指针或引用,并且类方法本身需要在父类中声明为virtual和子类声明。

请记住,如果不补偿MyList对象“拥有”(或应该拥有)传递给它的指针这一事实,使用这样的原始指针可能会导致严重的内存泄漏。 如果所有权不明确,您需要格外小心,或使用类似std::shared_ptr<T> 例如,如果你决定使用像std::vector这样的STL容器和原始指针,那么容器将不会“拥有”分配给每个指针的内存,并且当容器被销毁时,它将不会释放内存。它的每个成员指出,导致令人讨厌的内存泄漏。

顺便说一句,这是关于C ++的一个非常重要的观点......与C#/ Java不同,C ++使用显式而非隐式指针。 因此,如果您声明一个对象使其不是指针(即,它是堆栈上的静态或“自动”变量),那么如果将派生类实例对象复制到父实例中,您将最终“切片” “派生对象的父部分关闭,只是复制派生对象的父部分。 那不是你想要的。 您需要多态行为,因此必须使用指向父类类型的指针或引用。

例如,这是一个多态行为的工作示例

#include <iostream>

//polymorphic base
struct test
{
    virtual void print() { std::cout << "I'm the parent" << std::endl; }
};

//derived type
struct derived : public test
{
    virtual void print() { std::cout << "I'm the derived" << std::endl; }
};

int main()
{
    test* a = new test;
    test* b = new derived;

    a->print();
    b->print();  //calls derived::print through polymorphic behavior

    return 0;
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM