Setelah menghabiskan cukup banyak waktu untuk mengembangkan C #, saya perhatikan bahwa jika Anda mendeklarasikan kelas abstrak untuk tujuan menggunakannya sebagai antarmuka, Anda tidak dapat membuat contoh vektor kelas abstrak ini untuk menyimpan instance kelas anak.
#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;
};
Baris yang mendeklarasikan vektor kelas abstrak menyebabkan kesalahan ini di MS VS2005:
error C2259: 'IFunnyInterface' : cannot instantiate abstract class
Saya melihat solusi yang jelas, yaitu mengganti IFunnyInterface dengan yang berikut:
class IFunnyInterface
{
public:
virtual void IamFunny()
{
throw new std::exception("not implemented");
}
};
Apakah ini solusi yang dapat diterima C ++ bijaksana? Jika tidak, apakah ada pustaka pihak ketiga seperti boost yang dapat membantu saya mengatasi masalah ini?
Terima kasih telah membaca ini!
Anthony
sumber
Anda tidak dapat membuat vektor dari jenis kelas abstrak karena Anda tidak dapat membuat instance dari kelas abstrak, dan wadah C ++ Standard Library seperti std :: vector store values (yaitu instance). Jika Anda ingin melakukan ini, Anda harus membuat vektor pointer ke tipe kelas abstrak.
Lingkungan kerja Anda tidak akan berfungsi karena fungsi virtual (itulah sebabnya Anda menginginkan kelas abstrak di tempat pertama) hanya berfungsi ketika dipanggil melalui pointer atau referensi. Anda juga tidak dapat membuat vektor referensi, jadi ini adalah alasan kedua mengapa Anda harus menggunakan vektor pointer.
Anda harus menyadari bahwa C ++ dan C # memiliki sedikit kesamaan. Jika Anda berniat untuk mempelajari C ++, Anda harus memikirkannya dari awal, dan membaca tutorial C ++ khusus yang bagus seperti Accelerated C ++ oleh Koenig dan Moo.
sumber
Dalam hal ini kami tidak dapat menggunakan bahkan kode ini:
std::vector <IFunnyInterface*> funnyItems;
atau
std::vector <std::tr1::shared_ptr<IFunnyInterface> > funnyItems;
Karena tidak ada hubungan IS A antara FunnyImpl dan IFunnyInterface dan tidak ada konversi implisit antara FUnnyImpl dan IFunnyInterface karena warisan pribadi.
Anda harus memperbarui kode Anda sebagai berikut:
class IFunnyInterface { public: virtual void IamFunny() = 0; }; class FunnyImpl: public IFunnyInterface { public: virtual void IamFunny() { cout << "<INSERT JOKE HERE>"; } };
sumber
Alternatif tradisional adalah dengan menggunakan salah
vector
satu petunjuk, seperti yang telah disebutkan.Bagi mereka yang menghargai,
Boost
hadir dengan perpustakaanPointer Containers
yang sangat menarik: yang sangat cocok untuk tugas tersebut dan membebaskan Anda dari berbagai masalah yang tersirat oleh petunjuk:Perhatikan bahwa ini jauh lebih baik daripada
vector
smart pointer, baik dalam hal kinerja dan antarmuka.Sekarang, ada alternatif ketiga, yaitu mengubah hierarki Anda. Untuk isolasi pengguna yang lebih baik, saya telah melihat beberapa kali pola berikut digunakan:
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 { .. };
Ini cukup jelas, dan variasi
Pimpl
idiom diperkaya oleh sebuahStrategy
pola.Ia bekerja, tentu saja, hanya dalam kasus di mana Anda tidak ingin memanipulasi objek "sebenarnya" secara langsung, dan melibatkan deep-copy. Jadi itu mungkin bukan yang Anda inginkan.
sumber
Karena untuk mengubah ukuran vektor Anda perlu menggunakan konstruktor default dan ukuran kelas, yang pada gilirannya mengharuskannya menjadi konkret.
Anda dapat menggunakan penunjuk seperti yang disarankan lainnya.
sumber
std :: vector akan mencoba mengalokasikan memori untuk menampung tipe Anda. Jika kelas Anda murni virtual, vektor tidak dapat mengetahui ukuran kelas yang harus dialokasikan.
Saya pikir dengan solusi Anda, Anda akan dapat mengkompilasi
vector<IFunnyInterface>
tetapi Anda tidak akan dapat memanipulasi FunnyImpl di dalamnya. Misalnya jika IFunnyInterface (kelas abstrak) berukuran 20 (saya tidak begitu tahu) dan FunnyImpl berukuran 30 karena memiliki lebih banyak anggota dan kode, Anda akan mencoba memasukkan 30 ke dalam vektor 20 AndaSolusinya adalah dengan mengalokasikan memori di heap dengan "baru" dan menyimpan petunjuk di dalamnya
vector<IFunnyInterface*>
sumber
Saya pikir akar penyebab dari batasan yang sangat menyedihkan ini adalah kenyataan bahwa konstruktor tidak dapat virtual. Daripadanya penyusun tidak dapat menghasilkan kode yang menyalin objek tanpa mengetahui waktunya pada waktu kompilasi.
sumber