0

質問のこの部分は背景情報を提供するものであり、無視できます

私は、不思議なことに繰り返されるテンプレート パターンの使用に大きく依存するテンプレート ライブラリに取り組んでいます。クラス構造の背後にある考え方は、ユーザーが次のいずれかを実行できるということです。

1)。標準メソッドで事前定義されたクラスを使用します。これらのクラスは、コンストラクタ/デストラクタのみを提供し、変数メンバーを宣言し、基本クラスをフレンドとして宣言する基本クラスの非常に単純なリーフです。派生クラスの変数メンバーを操作するすべてのメソッドは、基本クラスで定義されます。

2)。基本クラスを使用して、独自の拡張機能を作成します。このメソッドにより、ユーザーは同じ変数メンバーで動作する独自のメソッドを導入することもできます。

設計により、単一レベルの継承のみが適用されます。

私の質問は、主に節 2 に関するものです。現在の実装では、ユーザーはすべてのコンストラクターを暗黙的に定義する必要があります (つまり、クラスの動的変数メンバーのメモリ割り当ての完全なプロセスを記述するなど)。

質問

以下の例は、CRTP を使用して、基本クラス コンストラクター内の派生クラスのヒープ変数のメモリ割り当ての定義を提供する可能性についての調査を示しています。

基本クラスの一部

template<class TLeafType, class MyClass> class sysBaseDiscreteTrajectoryPoint {
 ...

//one of the base constructors
sysBaseDiscreteTrajectoryPoint(const MyClass& MyClassInstance) {
    std::cout << "Base additional constructor called" << std::endl;
    std::cout << asLeaf().Point << std::endl;
    asLeaf().Point=new MyClass(MyClassInstance);
    std::cout << asLeaf().Point << std::endl;
}

TLeafType& asLeaf(void) {
return static_cast<TLeafType&>(*this);
}

...
};

派生クラス:

template<class MyClass> 
class sysDiscreteTrajectoryPoint: public sysBaseDiscreteTrajectoryPoint<sysDiscreteTrajectoryPoint<MyClass>, MyClass> {
...
friend class sysBaseDiscreteTrajectoryPoint<sysDiscreteTrajectoryPoint<MyClass>, MyClass>;
private:
    MyClass* Point;
public:
    sysDiscreteTrajectoryPoint(const MyClass& MyClassInstance): sysBaseDiscreteTrajectoryPoint<sysDiscreteTrajectoryPoint<MyClass>, MyClass>(MyClassInstance){
        std::cout << "Derived additional constructor called " << std::endl; 
        std::cout << Point << std::endl;
        std::cout << *Point << std::endl;
    }
...
}

主要:

int a(5);
sysDiscreteTrajectoryPoint<int> A(a);

コードは次の出力を生成します。

Base additional constructor called
0x847ff4
0x8737008
Derived additional constructor called 
0x8737008
5
Derived destructor called 
Base destructor called 

出力は、コンセプトが実現可能である可能性があることを示唆しています。ただし、2 つの質問があります。

1)。コードの実行中に発生するすべてのプロセスを理解していることを確認したいと思います。特に、上記のクラスからかなりの量のオブジェクトをインスタンス化する必要がある可能性があるため、プロセスの効率に関心があり、何が起こるかを理解したいPoint(隠された再定義はありますか?)

2)。boost質問は、派生クラスのメンバーのスマート ポインターを定義するためのライブラリの使用に関連しています。raw ポインターを に置き換えようとboost::shared_ptrしたときに、基本クラスを介して派生クラスのメンバーにメモリを割り当てようとすると、セグメンテーション フォールト エラーが発生しました。コードの重要なセクションを以下に示します。

基本クラスの一部:

template<class TLeafType, class MyClass> class sysBaseDiscreteTrajectoryPoint {
 ...

//one of the base constructors
sysBaseDiscreteTrajectoryPoint(const MyClass& MyClassInstance) {
    std::cout << "Base additional constructor called" << std::endl;
    std::cout << asLeaf().Point << std::endl;
    asLeaf().Point.reset(new MyClass(MyClassInstance));
    std::cout << asLeaf().Point << std::endl;
}

TLeafType& asLeaf(void) {
return static_cast<TLeafType&>(*this);
}

...
};

派生クラスの一部:

template<class MyClass> 
class sysDiscreteTrajectoryPoint: public sysBaseDiscreteTrajectoryPoint<sysDiscreteTrajectoryPoint<MyClass>, MyClass> {
...
friend class sysBaseDiscreteTrajectoryPoint<sysDiscreteTrajectoryPoint<MyClass>, MyClass>;
private:
    boost::shared_ptr<MyClass> Point;
public:
    sysDiscreteTrajectoryPoint(const MyClass& MyClassInstance): sysBaseDiscreteTrajectoryPoint<sysDiscreteTrajectoryPoint<MyClass>, MyClass>(MyClassInstance){
        std::cout << "Derived additional constructor called " << std::endl; 
        std::cout << Point << std::endl;
        std::cout << *Point << std::endl;
    }
...
}

主要:

int a(5);
sysDiscreteTrajectoryPoint<int> A(a);

コードは次の出力を生成します。

Base additional constructor called
0x28d324
Segmentation fault

私も試しscoped_ptrました。ただし、実行時に失敗しましたが、別のエラーが発生しました:

Base additional constructor called
*** glibc detected *** ./TestSystem: free(): invalid pointer: 0x00d3fff4 ***
======= Backtrace: =========
/lib/i386-linux-gnu/libc.so.6(+0x6b961)[0xc4e961]
...

ブーストスマートポインターの動作の詳細に関連していると思います。この問題を解決する方法を知っている人はいますか?

4

2 に答える 2

1

上記の回答で与えられた理由により、のアドレスshared_ptrコンパイル時にshared_ptr認識されますが、派生クラスのコンストラクターがまだ呼び出されておらず、そのインスタンス メンバーを暗黙的に呼び出す機会がなかったため、それ自体はまだ初期化されていません。のデフォルトのコンストラクターを含むコンストラクターshared_ptr。したがって、 を呼び出すreset()shared_ptr、新しいオブジェクトを割り当てて参照する前に、(既存の参照対象のリークを避けるために) に含まれている偽のアドレスでオブジェクトを解放 (および場合によっては削除) しようと最初に試みます。その最初のステップがセグメンテーション違反の原因だと私は信じています。

コンストラクターが最初に実行された場合、shared_ptr含まれている生のポインターが null になり、後続のreset()呼び出しが偽のアドレスでオブジェクトを解放しようとするのを防ぎます。

を使用asLeaf()して基本クラス コンストラクターから派生クラスにアクセスすることは、構造が不完全であるため (派生クラスのメンバーがまだ構築されていないため)、非 POD 型に対して本質的に安全ではありません。これは、偶然にも、基本コンストラクターからの仮想メソッド呼び出しが、より派生したクラスからのオーバーライドを決して呼び出さない理由です。ほとんどの場合、オブジェクト全体の状態がまだ定義されていません。

より良い解決策があるかもしれませんが、機能するアプローチの 1 つは、その初期化コードを基本クラスのコンストラクターから削除しinit()、派生クラスのインスタンス化ごとに明示的に呼び出される関数に配置することです。init()基本クラスに引き続き存在できますが、実行時にすべてが初期化されるため、より安全です。

補足:shared_ptr正当な理由なく小さなものを入れないようにしてください。この場合、正当な必要性があるかもしれませんが、一般的に、オーバーヘッドがエスカレートするため、可能な限り、単一所有者ポインターおよび共有ポインターへの単一所有者ポインターよりもメンバーの直接集約を好みます。単一所有者のポインターにはヒープ割り当てが含まれ、共有ポインターもこれに所有者のカウント/追跡のコストを追加して、到達不能なときにオブジェクトを削除できるようにします。

于 2012-07-31T07:30:32.040 に答える
0

Point基本コンストラクターから派生クラスに属するメンバーにアクセスするにはどうすればよいですか?基本コンストラクターが呼び出されているとき、派生クラス部分は存在しません。おそらくそれは「偶然」に機能します。

shared_ptrただし、初期化される前に割り当てようとするため、確かに失敗します。

于 2012-07-21T19:47:03.530 に答える