簡體   English   中英

為什么我們不能聲明一個 std::vector<abstractclass> ?</abstractclass>

[英]Why can't we declare a std::vector<AbstractClass>?

在 C# 中花費了相當長的時間進行開發后,我注意到如果您聲明一個抽象 class 以將其用作接口,則您無法實例化此抽象 class 的向量來存儲子類的實例。

#pragma once
#include <iostream>
#include <vector>

using namespace std;

class IFunnyInterface
{
public:
    virtual void IamFunny()  = 0;
};

class FunnyImpl: IFunnyInterface
{
public:
    virtual void IamFunny()
    {
        cout << "<INSERT JOKE HERE>";
    }
};

class FunnyContainer
{
private:
    std::vector <IFunnyInterface> funnyItems;
};

聲明抽象 class 向量的行在 MS VS2005 中導致此錯誤:

error C2259: 'IFunnyInterface' : cannot instantiate abstract class

我看到了一個明顯的解決方法,即將 IFunnyInterface 替換為以下內容:

class IFunnyInterface
{
public:
    virtual void IamFunny()
    {
        throw new std::exception("not implemented");
    }
};

這是一個可接受的解決方法 C++ 明智嗎? 如果沒有,是否有像 boost 這樣的第三方庫可以幫助我解決這個問題?

謝謝您閱讀此篇 !

安東尼

你不能實例化抽象類,因此抽象類的向量不能工作。

但是,您可以使用指向抽象類的指針向量:

std::vector<IFunnyInterface*> ifVec;

這也允許您實際使用多態行為——即使類不是抽象的,按值存儲也會導致對象切片的問題。

您不能創建抽象類類型的向量,因為您不能創建抽象類的實例,並且像 std::vector 這樣的 C++ 標准庫容器存儲值(即實例)。 如果你想這樣做,你必須創建一個指向抽象類類型的指針向量。

您的解決方法將不起作用,因為虛函數(這就是您首先想要抽象類的原因)僅在通過指針或引用調用時才起作用。 你也不能創建引用向量,所以這是你必須使用指針向量的第二個原因。

您應該意識到 C++ 和 C# 幾乎沒有共同之處。 如果您打算學習 C++,您應該將其視為從頭開始,並閱讀專門的 C++ 教程,例如 Koenig 和 Moo 的Accelerated C++

在這種情況下,我們甚至不能使用以下代碼:

std::vector <IFunnyInterface*> funnyItems;

或者

std::vector <std::tr1::shared_ptr<IFunnyInterface> > funnyItems;

因為FunnyImpl 和IFunnyInterface 之間沒有IS A 關系,並且由於私有繼承,FUnnyImpl 和IFunnyInterface 之間沒有隱式轉換。

您應該按如下方式更新您的代碼:

class IFunnyInterface
{
public:
    virtual void IamFunny()  = 0;
};

class FunnyImpl: public IFunnyInterface
{
public:
    virtual void IamFunny()
    {
        cout << "<INSERT JOKE HERE>";
    }
};

傳統的替代方法是使用指針vector ,就像已經提到的那樣。

對於那些欣賞的人, Boost附帶了一個非常有趣的庫: Pointer Containers ,它非常適合該任務,並使您擺脫指針隱含的各種問題:

  • 終身管理
  • 迭代器的雙重解引用

請注意,這在性能和接口方面都明顯優於智能指針vector

現在,還有第三種選擇,即更改您的層次結構。 為了更好地隔離用戶,我多次看到使用以下模式:

class IClass;

class MyClass
{
public:
  typedef enum { Var1, Var2 } Type;

  explicit MyClass(Type type);

  int foo();
  int bar();

private:
  IClass* m_impl;
};

struct IClass
{
  virtual ~IClass();

  virtual int foo();
  virtual int bar();
};

class MyClass1: public IClass { .. };
class MyClass2: public IClass { .. };

這非常簡單,並且是Pimpl習語的變體,並通過Strategy模式進行了豐富。

當然,它僅適用於您不希望直接操作“真實”對象並且涉及深復制的情況。 所以它可能不是你想要的。

因為要調整向量的大小,您需要使用默認構造函數和類的大小,這反過來又要求它是具體的。

您可以按照其他建議使用指針。

std::vector 將嘗試分配內存以包含您的類型。 如果您的類是純虛擬的,則向量無法知道它必須分配的類的大小。

我認為通過您的解決方法,您將能夠編譯vector<IFunnyInterface>但您將無法在其中操作 funnyImpl。 例如,如果 IFunnyInterface(抽象類)的大小為 20(我真的不知道),而 funnyImpl 的大小為 30,因為它有更多的成員和代碼,您最終將嘗試將 30 放入 20 的向量中

解決方案是使用“new”在堆上分配內存並將指針存儲在vector<IFunnyInterface*>

我認為這個真正可悲的限制的根本原因是構造函數不能虛擬。 因此,編譯器無法在不知道對象在編譯時的時間的情況下生成復制對象的代碼。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM