C++メモリ分布、コンパイルと実行段階の探索?(当分タイトル不明)

4024 ワード

前接Virtualメカニズム,多重継承といえば虚関数は完璧に互換性があるが,虚関数でないとできず,興味深い過程が現れたので,ここで検討する.
多重派生の場合,虚でない重名同パラメータリスト関数があると完全に継承できず,隠性事故を起こす可能性もある.
コードは次のとおりです.
#include<iostream>
#include<stdio.h>
using namespace std;
class A{
public:
        virtual ~A(){cout <<"A destruction"<<endl;}
        virtual void func(){cout <<"A func."<<endl;}
};
class A2{
public:
        virtual ~A2(){cout <<"A2 destruction"<<endl;}
        virtual void func(){cout <<"A2 func."<<endl;}
        void func2(){cout << "A2 func2."<<endl;}
};
class B{
public:
        virtual ~B(){cout <<"B destruction"<<endl;}
        virtual void func(){cout<<"B func."<<endl;}
};

class C:public A,public B
{
public:
        virtual ~C(){cout <<"C destruction"<<endl;}
        virtual void func(){cout <<"C func."<<endl;}
};
class D:public C
{
public:
        virtual ~D(){cout<<"D destruction"<<endl;}
        virtual void func(){cout<<"C func."<<endl;}
        void func2(){cout << "D func2."<<endl;}
};
class E:public A2,public D
{
public:
        virtual ~E(){cout <<"E destruction"<<endl;}
        void fooE(){}
        virtual void func(){cout <<"E func."<<endl;}
//      void func2(){cout << "E func2."<<endl;}
};
int main(){
        A a,a2;
        A2 a2222;
        B b,b2;
        C c,c2;
        D d,d2;
        E e,e2;
 //       e.func2();
        d.func2();
        a2222.func2();


        return 0;
}
注意、この時はコンパイルに成功しました!!!
しかしe.func 2()の注釈を放すとコンパイルできなくなる.
# g++ non_virtual_multi.cpp 
non_virtual_multi.cpp: In function ‘int main()’:
non_virtual_multi.cpp:50:   :   ‘func2’      
non_virtual_multi.cpp:33:   :candidates are: void D::func2()
non_virtual_multi.cpp:14:   :                void A2::func2()

この感覚は、1つの空のポインタからデータを要求するように、デフォルトの空のポインタは大丈夫で、式も大丈夫ですが、左の値でアドレスを要求するとcore dumpedがクラッシュします.(気をつけて、そこまで動いてやっと崩壊して、私は奇抜なペンの試験問題を見たことがあります.あなたは崩壊とは言えません.前に運行している印刷も書いて、それから崩壊しなければなりません~)
関数も、私は本当に呼び出してやっと問題を発見します(しかし、まだ実行時ではありません.呼び出し文をコードに書いたばかりで、コンパイルは通れません)
では、これは面白いですね.同じようにコンパイルして、呼び出しを書くか書かないかの2つです.
同じように、アクセス先が見つからないエラーです.1つはコンパイルエラーで、もう1つは(空のアドレスの値を取る)実行エラーです.
関数エントリアドレスについて:前の虚関数テーブルの絵は虚ポインタが虚表を指し、虚表が関数エントリを格納し、オブジェクトに対応する実装を見つけることができます.例えば、このようなものですか?A::func() B::func
では、これらの関数はどのように保存されているのか、あるいはどのように呼び出されているのか.専用のデータセグメント、コードセグメントなどがあることだけを覚えています.コードセグメントはメモリにロードされているに違いありません(または仮想メモリに変換されていますか?)関数を呼び出したい場合は、関数のアドレスを指定し、コードセグメント(ページを変えてメモリに呼び出す可能性があります)を実行します.
コンパイルの原理はあまり勉強していないので、でたらめに推測するしかありません.
この多重継承では,派生クラスはfunc 2()に実行のためのターゲット関数アドレスを探さなければならないが,誰をバインドするかには根拠がない.しかしコンパイルに成功したので,コンパイル段階でバインドされたfunc 2()ではない.正確には、クラスの宣言定義フェーズではありません.
呼び出し文が表示されてからリクエストの蜂起が要求される以上、このバインドはオブジェクトの宣言と定義フェーズでは発生しないようです.
あるいは、純粋なコンパイラ最適化では、コンパイラはもともとこの曖昧さを説明できない.呼び出さないとリスクがないと判断し,曖昧さを提示せず,呼び出されると曖昧さを提示する.このような解釈はもっと頼りになるが、結局はコンパイル段階なので、段階論では少し足が立たないような気がしますが、少なくとも私は説明があまりよくありません.
オブジェクト自身が曖昧関数を呼び出すことはできませんが、クラスポインタで呼び出すとしますか?コンパイルミスもなく、実行しても問題ありません.虚関数ではないので、ベースクラスポインタは派生クラスオブジェクトを指し、この関数を呼び出します.ベースクラスを探すfunc 2()を指します.これは曖昧ではありません.そうは測定できません.逆に言えば、派生クラスはベースクラスのオブジェクトを指すことが許されないので、仕方がありません.
空のポインタの内容にアクセスするクラッシュエラーについて.空のアドレスを取るのはそのステップで間違いないはずですが、なぜコンパイルフェーズポインタptrがNULLを付与したときに追跡しないのか、func()のようなメンバーを呼び出すコードがあることに気づいてコンパイルを間違えさせ、この多重継承と同じように処理しますか?追跡しにくいと思いますが、最初からNULLの付与を禁止することはできません.その後ptrが変わって、コンパイラができないかもしれません.また、この中にはダイナミックなものもあります.コンパイル時に知らないものがあります.例えば、プログラムの実行時に手動で値0を入力し、ポインタがこの値に付与されます.コンパイラは預言者を知らないわけにはいかない.クラスの関連関数エントリアドレスは、コンパイル段階でバインドされるべきです.
今日は先にここに着いて、疲れて、考えがありません.各種のメモリ分布、コード、データ、高低アドレス、各種の過程が空いていて、もう一度強固にします.メモリ分布という場所の知識点をもう少し総合して、まとめてみましょう.
コードセグメントをメモリにロードするか、ハードディスクキャッシュに捨てて実行するときにページを変更するかなどの問題があります.これはオペレーティングシステムが制御している可能性がありますが、実際に最適化できればメリットがあるので、設定できるかもしれません.