C++虚構造関数純虚構造関数虚構造関数


C++の中の構造関数は純粋な虚であることができますか?
周知のように、マルチステートを実現する過程で、ベースクラスの構造関数をvirtualに設定し、deleteのときにマルチステートのチェーン呼び出しを可能にする.では、構造関数は純虚に設定できますか?
class
 CBase
{
    public
:
        CBase()
       {
            printf("CBase()");
       }
    
virtual
 ~CBase() = 0;
};
答えはいいですが、このように実現する目的は何ですか.もちろんインスタンス化は避けます.
しかし、派生クラスはベースクラスの構造関数を実現することは不可能であるため、ベースクラスの構造関数は純虚としてマークすることができるが、構造関数を実現しなければならない.そうしないと、派生クラスは継承できず、コンパイルもできない.
次の詳細について説明します.
一.仮想構造関数オブジェクトの構造関数を正しく呼び出すためには、一般的に階層を持つ上位クラスがその構造関数を仮想関数として定義する必要があることを知っています.deleteの抽象クラスポインタでは、虚関数で真の構造関数を見つけなければならないからです.
次のようになります.
class Base
{
	public:
   		Base(){}
   		virtual ~Base(){}
};

class Derived: public Base
{
	public:
   		Derived(){};
   		~Derived(){};
}

void foo()
{
   Base *pb;
   pb = new Derived;
   delete pb;
} 

これは正しい使い方です.ダイナミックバインドが発生します.Derivedの構造関数を呼び出し、Baseの構造関数を呼び出します.
構造関数にvirtualを加えない場合、delete pbはBaseの構造関数のみを実行し、本物のDerived構造関数ではありません.
virtual関数ではないため、呼び出される関数は静的タイプ、すなわちBaseを指すことに依存する.
二.純虚解析関数の現在の問題は,Baseを抽象クラスとし,オブジェクトを直接構築することはできず,その中で純虚関数を定義する必要があるということである.他に適切な関数がない場合は、前のCObject定義を次のように変更して、構造関数を純虚として定義できます.
class Base
{
	public:
   		Base(){}
   		virtual ~Base()= 0
};

しかし,このコードはコンパイルできず,通常はlinkエラーであり,~Base()の参照(gccのエラーレポート)を見つけることができない.これは、構造関数、構造関数は他の内部関数とは異なり、呼び出し時にコンパイラが呼び出しチェーンを生成する必要があるためです.つまり、Derivedの解析関数には、Baseを呼び出した解析関数が隠されています.さっきのコードでは~Base()の関数体が欠けていて、もちろんエラーが発生します.
virtual f()=0という純虚関数文法は定義体のない意味だと考える誤りがある.
実は、これは間違っています.この構文は,この関数が純粋な虚関数であることを示すだけで,このクラスは抽象クラスになり,オブジェクトを生成できない.我々
純粋な虚関数の関数体を完全に指定できます.
通常の純粋な虚関数は関数体を必要としないのは,抽象クラスのこの関数を呼び出すのは一般的ではなく,派生クラスの対応する関数だけを呼び出すからである.これにより、純粋な虚析関数の関数体があり、上のコードは次のように変更する必要があります.
class Base
{
	public:
   		Base(){}
   		virtual ~Base() = 0; //pure virtual
};
Base::~Base()//function body
{
} 

文法的には、上記の構造関数をクラス宣言(インライン関数の書き方)に直接書き込むことはできません.これは直交化していない場所かもしれません.しかし、これは確かに少し面倒に見えます.
この問題は,一般にBaseでより適切な関数を見つけることができ,それを実装体のない純粋な虚関数として定義することによって,クラス全体を抽象クラスとして定義することができるため,いくつかの学術的に見える.しかし、この例のように、この技術にもいくつかの応用があります.
class Base  //abstract class
{
	public:
   		virtual ~Base(){};//virtual, not pure
   		virtual void Hiberarchy() const = 0;//pure virtual
};

void Base::Hiberarchy() const //pure virtual also can have function body
{
   std::cout <<"Base::Hiberarchy";
}

class Derived : public Base
{
	public:
   		Derived(){}
   		virtual void Hiberarchy() const
   		{
       		Base::Hiberarchy();
       		std::cout <<"Derived::Hiberarchy";
   		}
   		virtual void foo(){}
};


int main()
{
   Base* pb=new Derived();
   pb->Hiberarchy();
   pb->Base::Hiberarchy();
   return 0;
} 

この例では、クラスの継承関係を印刷しようとします.基底クラスに虚関数Hiberarchyを定義し、派生クラスごとにこの関数を再ロードします.私たちはもう一度、Baseを抽象的なクラスにしたいため、このクラスには他の適切な方法のメンバーが純虚と定義できるものはありません.私たちはHiberarchyを純虚と定義するしかありません.(もちろん、~Base関数は完全に定義できますが、これは上記の議論と同じです.^^)
また、mainには2つの呼び出し方法があり、1つ目は通常の方法で動的リンクを行い、虚関数を実行し、結果「Derived::Hiberarchy」;2つ目はクラスを指定する方法で、虚関数のダイナミックリンクプロセスは実行されません.結果は「Base::Hiberarchy」です.
上記の分析から分かるように、
純粋な虚関数を定義する本当の目的は抽象クラスを定義することです
関数そのものではありません.これとは対照的にjavaでは抽象クラスを定義する文法はabstract classであり、つまりクラスの1級で指定されている(もちろん虚関数かabstractキーワードも加わる).この方法がもっと良いのではないでしょうか.Stroustrupの「C++言語の設計と進化」で私はこのような話を見つけました.
「私が選んだのは、個々の関数を純粋な虚として記述する方法であり、完全なクラス宣言を抽象的な形式として定義していない.これは純粋な虚関数の概念がより柔軟であるためである.私は段階的にクラスを定義できる能力を重視している.つまり、事前に純粋な虚関数を定義し、さらなる派生クラスに残して定義することもあることを発見した.使用する」.
私はまだ後の一言を完全に理解していないので、別の角度からこの概念を述べたいと思っています.すなわち,多層複雑なクラス構造では,中間階層のクラスは抽象関数を具体化すべきであるが,すべてではない可能性が高い.中間クラスは,すべての虚関数を具体化したかどうか,およびその祖先がどの関数を具体化したかを知る必要はなく,自分の職責に注目すればよい.すなわち,中間クラスは自分が真の抽象クラスであるかどうかを知る必要はなく,設計者はこの中間クラスのクラスレベルにabstractのような説明を加える必要があるかどうかを考慮する必要もない.
もちろん、一つの言語の設計には多くの要素があり、良し悪しは各方面にある.これはただの解釈にすぎない.
最後に、虚関数に関するよくある質問をまとめます.
1)虚関数は動的にバインドされており,すなわち,虚関数のポインタと参照を用いてクラスを定義する関数ではなく,実際のクラスの対応する関数を正しく見つけることができる.これは虚関数の基本機能であり,もう説明しない. 
2) 
コンストラクション関数は虚関数ではありません
.そして、
コンストラクション関数で虚関数を呼び出し、実際に親の対応する関数を実行します.
ああ、自分はまだ构造ができていないので、多态はdisableされています. 
3) 
構造関数は虚関数であってもよい
また、複雑なクラス構造では、これが必要になることが多い.
 
4)1つの関数を純虚関数として定義し,実際にはこのクラスを抽象クラスとして定義し,オブジェクトをインスタンス化できない. 
5) 
純粋な虚関数には通常定義体はありませんが、完全に持つことができます.
.
6)解析関数は純粋な虚であってもよいが
純粋な虚構造関数には定義体が必要です
解析関数の呼び出しはサブクラスに隠されているためです. 
7)非純粋な虚関数には定義体が必要であり,そうでなければ誤りである. 
8)派生クラスのoverride虚関数定義は、親クラスと完全に一致する必要があります.
例外を除いて、親クラスの戻り値がポインタまたは参照の場合、サブクラスoverrideはこのポインタ(または参照)の派生を返すことができます.
.例えば、上記の例では、Baseでvirtual Base*clone()が定義されている.Derivedではvirtual Derived*clone()として定義できます.このリラックスはCloneモードにとって非常に有用であることがわかる. 
変換元:http://blog.csdn.net/fisher_jiang/article/details/2477577