簡體   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