简体   繁体   English

C ++继承函数覆盖

[英]C++ inheritance function override

I am pretty new to C++ and I am coming from Java/C#. 我是C ++的新手,我来自Java / C#。

I know that in Java and C# you can make a class, have another class inherit from it, and override its function. 我知道在Java和C#中你可以创建一个类,让另一个类继承它,并覆盖它的功能。 You can then make list of the parent class and insert to this list objects of the child class. 然后,您可以创建父类的列表,并将子类的对象插入此列表中。 After that you can use it's functions that were overridden. 之后,您可以使用被覆盖的功能。

Example: 例:

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

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

Usage: 用法:

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

Output: 输出:

 test2 

In C++, when I do this with std::vector , it calls the parent's member function, rather than that of the child. 在C ++中, 当我使用std::vector执行此操作时 ,它调用父代的成员函数,而不是子函数。

How can I do the above in C++? 如何在C ++中完成上述操作?

I feel like there's two problems here for you. 我觉得这里有两个问题。 One is a syntactical problem, which others have already addressed. 一个是语法问题,其他人已经解决了。 However, it seems that you also have an underlying problem of trying to write Java/C# code in C++ . 但是,您似乎也有尝试用C ++编写Java / C#代码的根本问题。 This will lead to misery no matter what the syntactical problems are, so I try to address this here. 无论语法问题是什么,这都会导致痛苦,所以我试着在这里解决这个问题。

In c++ when I do this with vector it calls that parent's function. 在c ++中,当我使用vector执行此操作时,它会调用父项的函数。 How can I do the example above in C++? 我怎样才能在C ++中做上面的例子?

Java and C# use the object-oriented paradigm for everything . Java和C#使用面向对象的范例来实现一切 C++ is different in that C++ is a multi-paradigm language . C ++的不同之处在于C ++是一种多范式语言 It supports (more or less) Structured, OO, Generic, Functional and whatnot Programming paradigms. 它支持(或多或少)结构化,面向对象,通用,功能和诸如编程范例。 You can freely mix and mingle paradigms , and C++ shines brightest where you do that. 你可以自由地混合和混合范式 ,而C ++在你做到这一点的时候会闪亮一点。

The part of the standard library that derives from the STL, that is: containers, algorithms, iterators, are not OO at all. 源自STL的标准库的一部分,即:容器,算法,迭代器,根本不是OO。 They are applying Generic Programming . 他们正在应用通用编程 One of the attributes of that is that the containers usually (there are exceptions, but not within the standard library itself) store values , rather than references . 其中一个属性是容器通常(有异常,但不在标准库本身内)存储 ,而不是引用 Polymorphism, however, at least runtime polymorphism, only operates on references (or, syntactically, pointers, which are, semantically, references, too). 然而,多态性,至少是运行时多态性,仅对引用 (或语法上的指针,也是语义上的引用)进行操作。

If you have a std::vector<base_class> vc , this will store actual values , rather than references to objects somewhere on the heap. 如果你有一个std::vector<base_class> vc ,这将存储实际 ,而不是对堆上某处对象的引用。 If you put an object into such a container, the object will actually be copied into the container. 如果将对象放入这样的容器中,该对象实际上将被复制到容器中。 If you put in a derived_class object, then that is subjected to slicing . 如果你输入一个derived_class对象,那么它将被切片 That is, only the base_class part of it will be copied into the container, all the derived_class parts will be disregarded. 也就是说,只有它的base_class部分将被复制到容器中,所有的derived_class部分都将被忽略。 You then end up with an actual base_class object in the container, rather than, as in Java and C#, a base class reference to a derived class object somewhere on the heap. 然后,您最终在容器中得到一个实际的base_class对象,而不是像在Java和C#中那样,是对堆上某个派生类对象的基类引用。
That is why invoking a member function on that object will end up in the base class: there is no derived class object to invoke a function on . 这就是为什么在该对象上调用成员函数将最终在基类中: 没有派生类对象来调用函数

In C++, if you want to employ OOP , you will usually have to dynamically allocate derived class objects (ie, new derived_class() ) and assign them to base class pointers. 在C ++中, 如果要使用OOP ,通常必须动态分配派生类对象 (即new derived_class() )并将它们分配给基类指针。 The problem with this is that C++ does not have garbage collection, so you must keep track of those pointers, and all the copies made from it, and explicitly delete the object just before the last pointer gets destroyed. 这个问题是C ++没有垃圾收集,所以你必须跟踪那些指针,以及从中生成的所有副本,并在最后一个指针被销毁之前显式删除对象。 That is very error-prone to do manually , which is why nowadays everybody lets smart pointers do this automatically. 手动操作非常容易出错 ,这就是为什么现在每个人都让智能指针自动执行此操作。

So what you want is std::vector<smart_ptr<base_class>> and put in new derived_class() objects. 所以你想要的是std::vector<smart_ptr<base_class>>并放入new derived_class()对象。 What the symbolic smart_ptr refers to depends on your needs. 符号smart_ptr所指的内容取决于您的需求。 If you plan to store pointers to those objects nowhere but in that container , std::unique_ptr ( std::tr1::unique_ptr if your compiler only supports C++03, or boost::unique_ptr if it doesn't even support that) would be ideal. 如果你计划在那个容器中存储指向那些对象的指针,那么std::unique_ptrstd::tr1::unique_ptr如果你的编译器只支持C ++ 03,或者boost::unique_ptr如果它甚至不支持那个)会是理想的。 If you freely pass around such pointers, and them to keep track of when the last goes out of scope for themselves, std::shared_ptr would be better. 如果你自由地传递这样的指针,并且它们跟踪最后一次超出自己的范围, std::shared_ptr会更好。


Now, all this said, I feel the need to add: You might not need to do this the OO way at all . 现在,所有这些说,我觉得有必要补充: 你可能根本不需要OO方式 There might be a much better design if you could just let go of the rigid OO thinking Java and C# have imprisoned you in. 如果你可以放弃刚刚认真考虑Java和C#监禁你的OO,可能会有更好的设计。

If you employ polymorphism just so you can pass containers with different content to the same algorithms, then employing Generic Programming might be much better : 如果您使用多态,那么您可以将具有不同内容的容器传递给相同的算法,那么使用通用编程可能会好得多

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());

This works for all types (here it's some_class ) that have a foo() member not taking any arguments and returning something comparable with whatever bar() returns, and have a baz() member, not taking any arguments either. 这适用于所有类型(这里是some_class ),其中foo()成员不接受任何参数并返回与bar()返回的内容相当的东西,并且具有baz()成员,也不接受任何参数。 (If you try to use some type that doesn't have those, the compiler will bark at you.) (如果你尝试使用某些没有那些类型的类型,编译器会咆哮你。)

Unlike Java or C#, C++ uses value semantics by default. 与Java或C#不同,C ++默认使用值语义。 An std::vector<Parent> contains actual objects of Parent type, and not pointers or references. std::vector<Parent>包含Parent类型的实际对象,而不包含指针或引用。 When you insert into the vector, the object you are inserting is copied, and it is copied into an object of Parent type. 插入向量时,将复制要插入的对象,并将其复制到Parent类型的对象中。 (Objects can't change type.) This is called slicing. (对象不能更改类型。)这称为切片。

If you want to use polymorphism in C++, it is necessary that you specify explicitly that you want reference semantics. 如果要在C ++中使用多态,则必须明确指定要引用语义。 Both pointers and references provide reference semantics, and it's possible to define "smart pointers"—classes which behave like a pointer to some other class. 指针和引用都提供了引用语义,并且可以定义“智能指针” - 类似于指向其他类的指针的类。 Since references don't support the copy/assignment semantics required by the standard containers, they can't be used to instantiate a container, so if the container is to hold polymorphic objects, it must be defined to contain pointers. 由于引用不支持标准容器所需的复制/赋值语义,因此它们不能用于实例化容器,因此如果容器要容纳多态对象,则必须将其定义为包含指针。 So: 所以:

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

but

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

Because of slicing, polymorphism and copy/assignment don't work well together, and it is usual to block copy/assignment in classes designed to be the base of a hierarchy. 由于切片,多态性和复制/赋值不能很好地协同工作,并且通常在设计为层次结构基础的类中阻止复制/赋值。

Also, if you are going to manage objects through pointers to the base class, the destructor should be virtual: 此外,如果要通过指向基类的指针来管理对象,则析构函数应该是虚拟的:

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

Otherwise, you'll run into undefined behavior when you go to delete the object (through a pointer to its base). 否则,当您删除对象时(通过指向其基础的指针),您将遇到未定义的行为。

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

It looks like the boost::ptr_container library would be very helpful for you. 看起来boost::ptr_container库对你非常有帮助。 It works in the same manner as a vector of (smart) pointers but it has the added benefit of having syntax designed for use as such. 它的工作方式与(智能)指针的向量相同,但它具有设计用于此类的语法的额外好处。

So for example you can do the following: 例如,您可以执行以下操作:

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 );

Which will call the derived class implementation of the virtual memberFunction . 这将调用virtual memberFunction的派生类实现。

C++ doesn't have an override keyword. C ++没有override关键字。 Just redeclare your overridden method as virtual . 只需将重写的方法重新声明为virtual方法。

In C++, this would look something like the following: 在C ++中,这看起来如下所示:

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

In order to invoke polymorphic functionality in C++ where you would call the child's function and not the parents, you have to use either pointers or references that point or reference the parent class, and the class methods themselves need to be declared as virtual in the parent and child class declarations. 为了在C ++中调用多态函数,您将调用子函数而不是父函数,您必须使用指向或引用父类的指针或引用,并且类方法本身需要在父类中声明为virtual和子类声明。

Keep in mind that using raw pointers like this could cause some serious memory leaks if you do not compensate for the fact that the MyList object "owns" (or should own) the pointers being passed to it. 请记住,如果不补偿MyList对象“拥有”(或应该拥有)传递给它的指针这一事实,使用这样的原始指针可能会导致严重的内存泄漏。 If ownership is ambiguous, you need to be extra careful, or use something like std::shared_ptr<T> . 如果所有权不明确,您需要格外小心,或使用类似std::shared_ptr<T> For instance, if you decided to use a STL container like std::vector with raw-pointers, then the container will not "own" the memory allocated to each pointer, and when the container is destroyed, it will not free the memory being pointed to by each of it's members, resulting in a nasty memory leak. 例如,如果你决定使用像std::vector这样的STL容器和原始指针,那么容器将不会“拥有”分配给每个指针的内存,并且当容器被销毁时,它将不会释放内存。它的每个成员指出,导致令人讨厌的内存泄漏。

BTW, this is a very important point about C++ ... unlike C#/Java, C++ uses explicit, not implicit pointers. 顺便说一句,这是关于C ++的一个非常重要的观点......与C#/ Java不同,C ++使用显式而非隐式指针。 Therefore if you declare an object such that it is not a pointer (ie, it's a static, or "automatic" variable on the stack), then if you copy a derived class instance object into a parent instance, you will end up "slicing" the parent portion of the derived object off, and just copying the parent portion of the derived object. 因此,如果您声明一个对象使其不是指针(即,它是堆栈上的静态或“自动”变量),那么如果将派生类实例对象复制到父实例中,您将最终“切片” “派生对象的父部分关闭,只是复制派生对象的父部分。 That's not what you want. 那不是你想要的。 You want polymorphic behavior, and therefore you must use pointers or references to the parent class-type. 您需要多态行为,因此必须使用指向父类类型的指针或引用。

For instance, here is a working example of polymorphic behavior: 例如,这是一个多态行为的工作示例

#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