C ++继承函数覆盖

时间:2012-06-25 11:59:24

标签: c++

我是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 ++中完成上述操作?

6 个答案:

答案 0 :(得分:8)

我觉得这里有两个问题。一个是语法问题,其他人已经解决了。但是,您似乎还有一个潜在的问题: 尝试在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方式执行此操作 。如果您可以放弃严格的OO思维Java和C#监禁你,可能会有更好的设计。

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

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()成员,也没有任何参数。 (如果你尝试使用某些没有那些类型的类型,编译器会咆哮你。)

答案 1 :(得分:3)

与Java或C#不同,C ++默认使用值语义。一个 std::vector<Parent>包含Parent类型的实际对象,而不包含Parent类型的实际对象 指针或引用。当你插入向量时,对象就是你 正在插入被复制,并被复制到std::vector<ValueType> v; v.push_back( ValueType() ); // no new 的对象中 类型。 (对象不能更改类型。)这称为切片。

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

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

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

由于切片,多态性和复制/赋值不能很好地工作 在一起,通常在设计的类中阻止复制/分配 成为等级制的基础。

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

{{1}}

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

答案 2 :(得分:1)

test()应在virtual课程中Parent,以确保调用Child班级test()

答案 3 :(得分:1)

看起来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 );

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

答案 4 :(得分:0)

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

答案 5 :(得分:0)

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

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

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

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

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

例如,这是一个working example多态行为:

std::vector