では、純粋仮想関数を持つ基本クラスを継承する 2 つのクラスがあるとします。両方のクラスは、その関数の独自のバージョンを実装しますが、追加のメンバー変数を追加しないため、同じサイズになります。時々、プログラムの実行中に、すべてのデータをコピーせずに、あるクラスを別のクラスに変換したいことがあります。なので基本的には他のクラスの仮想テーブルを利用させたいと思っています。これを行うポータブルな方法はありますか?
7 に答える
これを行う移植可能な方法は、実際にコピー可能な仮想ポインターを持つ独自のクラス システムを実装することです。
標準 C++ には仮想ポインタのようなものはありません。
ノルウェーの Andersen Consulting (現 Accenture) の若い同僚が、深刻な問題について私に相談したことがあります。Visual Basic で開発された彼らのアプリケーションは、読み込みに非常に長い時間がかかりました。彼は、これは各クラスを独自の DLL に配置したためではないかと疑っていました。
最悪の事態を恐れて、私はさらに尋ねました。そして、はい、彼らはまた、任意のクラッシュなどの問題を抱えていました.
彼は、他の方法では説明のつかないクラッシュが、vtable ポインターを置き換えることによって、実行時にオブジェクトの型を変更するための巧妙なスキームに関連している可能性があるのではないかと疑っていました。
私は、彼らが本当にこれらのことをするべきではないかもしれないと提案しました. 彼は懐疑的な目で私を見て、もう一度ゼロからやり直す時間はないと言いました。実際、彼らはすでにそれを拡大しており、プロジェクトリーダーが義務的な会議に参加する代わりにクライアントサイトで作業することを主張するなど、さまざまな問題がありました. 私には、それはきのこの管理のように聞こえました (暗闇の中に保管し、頭が飛び出したらそれを切ります): これらのことはしばしば一緒に行われます.
とにかく、私はあなたに同じアドバイスをします。
おそらく、代わりに高速移動操作を実装して、データをaからbに移動できますか?
それとも、すべてが時期尚早の最適化のケースであることがわかるでしょうか?
いいえ。言語に関する限り、仮想テーブルのようなものはなく、見た目/内容/保存場所に関するルールは言うまでもありません。
なんらかの形式の構成が、おそらくあなたのタスクにより適しています。
これを行うポータブルな方法はありますか?
絶対違う。仮想関数の実装方法の詳細は仕様で定義されていないため、ある仮想クラスが別の仮想クラスであると見なす移植可能な方法はありません。
他の回答が言っているように、実際に vtable を変更することは間違いなく移植性がありません。
ただし、クラス タイプを実際に変更せずに同様のセマンティクスを実現できる回避策がいくつかあります。
この最も簡単な解決策は、現在の実装を記述する列挙型を使用して「独自の」継承を行うことです。
class MyClass
{
public:
enum DerivedType { A, B };
private:
DerivedType myType;
public:
void myVirtualFunction()
{
if (myType == A)
myAFunction();
else
myBFunction();
}
}
関数ポインターをパブリック メンバー変数として使用することもできます。これは、クラスの型を示す関数に設定されます。次に、関数ポインタを他のクラスの関数に設定して、「型を変更する」ことができます
データのコピーを避けたいと述べたので、さまざまなクラスを保持できますが、すべてのメンバー変数への参照カウント ポインターを使用して、互いに反対の型の新しいオブジェクトをすばやく作成できます。
この質問は古いですが、これを行う方法を考えてみたいと思います。(携帯性はよくわかりません)
私が理解していることから、クラスがB
あり、C
それはいくつかのクラスから継承されA
、それらの間に単一の仮想関数のみが存在します。(ここで紹介する方法は、 と が関連していない場合B
にC
も機能します。)
class A {
public:
virtual std::string hello() = 0;
};
class B : public A {
public:
virtual std::string hello() { return "B"; }
};
class C : public A {
public:
virtual std::string hello() { return "C"; }
};
そして、B
to をC
then コールhello
して get を取得し"B"
ます。
boost::any
したがって、適合する限り、何でもキャストする簡易バージョンの を作成する方法があります :)
struct parent {};
template< typename T >
struct child : public parent {
child(T const& t): item(t){}
mutable T item;
};
template< typename T >
T& as(parent const & p) { return static_cast< child< T > const& >(p).item; }
次に、すべてを混ぜ合わせます。
B b;
parent* p = new child< B >(b);
std::cout << as< C >(*p).hello() << std::endl;
// ==== OUTPUT ====
// B
ここでコードの動作を確認できます。
さらに一歩進んで、あるタイプから別のタイプに変換する関数を作成することができます。
template< typename TO, typename FROM >
TO& convert(FROM const& from) {
parent* p = new child< FROM >(from);
return as< TO >(p);
};
これはここで実行できます。
(これらのコード例のリンクで継承を見逃していることに気付きましたが、質問を読んだ後、それが実際に望まれていたことだと思います。したがって、継承なしのテストを見るには、ここに移動します)
私が遊んで始めた他のコードもいくつか役立つかもしれないと思いました...
#include <iostream>
#include <string>
class B {
public:
virtual char hello() {return 'B';}
};
class C {
public:
virtual int hello() {return 65;}
};
struct parent {};
template< typename T >
struct child : public parent {
child(T const& t): item(t){}
mutable T item;
};
template< typename T >
T& as(parent const & p) { return static_cast< child< T > const& >(p).item; }
template< typename TO, typename FROM >
TO& convert(FROM const& from) {
parent* p = new child< FROM >(from);
return as< TO >(*p);
};
int main()
{
B b;
std::cout << convert< C, B >(b).hello() << std::endl;
C c;
std::cout << convert< B, C >(c).hello() << std::endl;
}
// ==== OUTPUT ====
// 66
// A
convert 関数内ですべてを作成する方法を考え出しました。
template< typename TO, typename FROM >
TO& convert(FROM const& from) {
struct parent {};
struct child : public parent {
child(FROM const& t): item(t){}
mutable FROM item;
};
struct sibling : public parent {
sibling(TO const& t): item(t){}
mutable TO item;
};
parent* p = new child(from);
return static_cast< sibling const& >(*p).item;
};