基礎:C++の多態体系


C++の多態系
  • 4.7.1多態の基本概念
  • 4.7.2マルチステートケース1-計算機クラス
  • 4.7.3純虚関数と抽象クラス
  • 4.7.4多態ケース2-飲み物を作る
  • 4.7.5虚析と純虚析
  • 4.7.6マルチステートケース3-コンピュータ組立

  • 4.7.1多態の基本概念
    マルチステートはC++オブジェクト向けの3つの特性の1つです
    多態は二つに分類される
    静的マルチステート:関数のリロードと演算子のリロードは静的マルチステートに属し、関数名のダイナミックマルチステートを多重化します:派生クラスと虚関数の実行時マルチステートの実装
    静的マルチステートと動的マルチステートの違い:
    静的マルチステートの関数アドレス早バインド-コンパイルフェーズ関数アドレス動的マルチステートの関数アドレス晩バインド-実行フェーズ決定関数アドレス次の例で説明するマルチステート
    class Animal
    {
         
    public:
    	//Speak       
    	//      virtual   ,     ,                     。
    	virtual void speak()
    	{
         
    		cout << "     " << endl;
    	}
    };
    
    class Cat :public Animal
    {
         
    public:
    	void speak()
    	{
         
    		cout << "     " << endl;
    	}
    };
    
    class Dog :public Animal
    {
         
    public:
    
    	void speak()
    	{
         
    		cout << "     " << endl;
    	}
    
    };
    //          ,            
    //               ,      
    //               ,      
    
    void DoSpeak(Animal & animal)
    {
         
    	animal.speak();
    }
    //
    //      : 
    //1、     
    //2、           
    //    :
    //             
    
    void test01()
    {
         
    	Cat cat;
    	DoSpeak(cat);
    
    
    	Dog dog;
    	DoSpeak(dog);
    }
    
    
    int main() {
         
    
    	test01();
    
    	system("pause");
    
    	return 0;
    }
    

    まとめ:
    たじょうたいじょうけん
    継承関係のあるサブクラス親の虚関数を書き換える
    たじょうたいしようじょうけん
    親ポインタまたは参照子オブジェクトへの書き換え:関数戻り値タイプ関数名パラメータリストが完全に一致することを書き換えと呼びます
    4.7.2マルチステートケース一-計算機クラス
    ケースの説明:
    通常の書き方と多態技術をそれぞれ利用して,2つの操作数を演算する計算機クラスを設計した.
    マルチステートの利点:
    コード組織構造の明確な可読性は、前期と後期の拡張とメンテナンスの例に優れています.
    //    
    class Calculator {
         
    public:
    	int getResult(string oper)
    	{
         
    		if (oper == "+") {
         
    			return m_Num1 + m_Num2;
    		}
    		else if (oper == "-") {
         
    			return m_Num1 - m_Num2;
    		}
    		else if (oper == "*") {
         
    			return m_Num1 * m_Num2;
    		}
    		//         ,      
    	}
    public:
    	int m_Num1;
    	int m_Num2;
    };
    
    void test01()
    {
         
    	//      
    	Calculator c;
    	c.m_Num1 = 10;
    	c.m_Num2 = 10;
    	cout << c.m_Num1 << " + " << c.m_Num2 << " = " << c.getResult("+") << endl;
    
    	cout << c.m_Num1 << " - " << c.m_Num2 << " = " << c.getResult("-") << endl;
    
    	cout << c.m_Num1 << " * " << c.m_Num2 << " = " << c.getResult("*") << endl;
    }
    
    
    
    //    
    //      
    //    :        ,    ,              
    class AbstractCalculator
    {
         
    public :
    
    	virtual int getResult()
    	{
         
    		return 0;
    	}
    
    	int m_Num1;
    	int m_Num2;
    };
    
    //     
    class AddCalculator :public AbstractCalculator
    {
         
    public:
    	int getResult()
    	{
         
    		return m_Num1 + m_Num2;
    	}
    };
    
    //     
    class SubCalculator :public AbstractCalculator
    {
         
    public:
    	int getResult()
    	{
         
    		return m_Num1 - m_Num2;
    	}
    };
    
    //     
    class MulCalculator :public AbstractCalculator
    {
         
    public:
    	int getResult()
    	{
         
    		return m_Num1 * m_Num2;
    	}
    };
    
    
    void test02()
    {
         
    	//       
    	AbstractCalculator *abc = new AddCalculator;
    	abc->m_Num1 = 10;
    	abc->m_Num2 = 10;
    	cout << abc->m_Num1 << " + " << abc->m_Num2 << " = " << abc->getResult() << endl;
    	delete abc;  //       
    
    	//       
    	abc = new SubCalculator;
    	abc->m_Num1 = 10;
    	abc->m_Num2 = 10;
    	cout << abc->m_Num1 << " - " << abc->m_Num2 << " = " << abc->getResult() << endl;
    	delete abc;  
    
    	//       
    	abc = new MulCalculator;
    	abc->m_Num1 = 10;
    	abc->m_Num2 = 10;
    	cout << abc->m_Num1 << " * " << abc->m_Num2 << " = " << abc->getResult() << endl;
    	delete abc;
    }
    
    int main() {
         
    
    	//test01();
    
    	test02();
    
    	system("pause");
    
    	return 0;
    }
    

    まとめ:C++開発はマルチステート設計プログラムアーキテクチャの利用を提唱している.マルチステートの利点が多いからだ.
    4.7.3純虚関数と抽象クラス
    マルチステートでは、通常、親クラスの虚関数の実装は意味がなく、主にサブクラスの書き換えを呼び出す内容である.
    したがって、虚関数を純虚関数に変更することができます.
    純虚関数構文:virtual戻り値タイプ関数名(パラメータリスト)=0;
    クラスに純粋な虚関数がある場合、このクラスは抽象クラスとも呼ばれます.
    抽象クラスの特徴:
    インスタンス化できないオブジェクトのサブクラスは、抽象クラスの純粋な虚関数を書き換える必要があります.そうしないと、抽象クラスの例にもなります.
    class Base
    {
         
    public:
    	//    
    	//                 
    	//          
    	//              ,        
    	virtual void func() = 0;
    };
    
    class Son :public Base
    {
         
    public:
    	virtual void func() 
    	{
         
    		cout << "func  " << endl;
    	};
    };
    
    void test01()
    {
         
    	Base * base = NULL;
    	//base = new Base; //   ,          
    	base = new Son;
    	base->func();
    	delete base;//    
    }
    
    int main() {
         
    
    	test01();
    
    	system("pause");
    
    	return 0;
    }
    

    4.7.4多態ケース2-飲み物を作る
    ケースの説明:
    飲み物を作る大まかな流れは、水を煮る-泡立てる-カップに注ぐ-補助材料を加える
    多態技術を用いて本事例を実現し,抽象的な飲料ベース類の製造を提供し,サブ類のコーヒーと茶葉の製造を提供する
    例:
    //      
    class AbstractDrinking {
         
    public:
    	//  
    	virtual void Boil() = 0;
    	//  
    	virtual void Brew() = 0;
    	//    
    	virtual void PourInCup() = 0;
    	//    
    	virtual void PutSomething() = 0;
    	//    
    	void MakeDrink() {
         
    		Boil();
    		Brew();
    		PourInCup();
    		PutSomething();
    	}
    };
    
    //    
    class Coffee : public AbstractDrinking {
         
    public:
    	//  
    	virtual void Boil() {
         
    		cout << "     !" << endl;
    	}
    	//  
    	virtual void Brew() {
         
    		cout << "    !" << endl;
    	}
    	//    
    	virtual void PourInCup() {
         
    		cout << "       !" << endl;
    	}
    	//    
    	virtual void PutSomething() {
         
    		cout << "    !" << endl;
    	}
    };
    
    //    
    class Tea : public AbstractDrinking {
         
    public:
    	//  
    	virtual void Boil() {
         
    		cout << "    !" << endl;
    	}
    	//  
    	virtual void Brew() {
         
    		cout << "    !" << endl;
    	}
    	//    
    	virtual void PourInCup() {
         
    		cout << "       !" << endl;
    	}
    	//    
    	virtual void PutSomething() {
         
    		cout << "    !" << endl;
    	}
    };
    
    //    
    void DoWork(AbstractDrinking* drink) {
         
    	drink->MakeDrink();
    	delete drink;
    }
    
    void test01() {
         
    	DoWork(new Coffee);
    	cout << "--------------" << endl;
    	DoWork(new Tea);
    }
    
    
    int main() {
         
    
    	test01();
    
    	system("pause");
    
    	return 0;
    }
    

    4.7.5虚析構造と純虚析構造
    マルチステートで使用する場合、サブクラスに属性がヒープ領域に開いている場合、親ポインタは解放時にサブクラスのプロファイルコードを呼び出すことができません.
    解決策:親クラスの構造関数を虚構造または純虚構造に変更する
    虚析と純虚析の共通性:
    親ポインタが子オブジェクトを解放するには、虚析と純虚析の区別を実現するために特定の関数が必要です.
    純粋なダミー構造の場合、このクラスは抽象クラスに属し、オブジェクトのダミー構造構文をインスタンス化できません.
    virtual~クラス名(){}
    純虚解析構文:
    virtual~クラス名()=0;
    クラス名::~クラス名(){}
    例:
    class Animal {
         
    public:
    
    	Animal()
    	{
         
    		cout << "Animal       !" << endl;
    	}
    	virtual void Speak() = 0;
    
    	//      virtual   ,       
    	//virtual ~Animal()
    	//{
         
    	//	cout << "Animal       !" << endl;
    	//}
    
    
    	virtual ~Animal() = 0;
    };
    
    Animal::~Animal()
    {
         
    	cout << "Animal         !" << endl;
    }
    
    //             ,                  。       。
    
    class Cat : public Animal {
         
    public:
    	Cat(string name)
    	{
         
    		cout << "Cat      !" << endl;
    		m_Name = new string(name);
    	}
    	virtual void Speak()
    	{
         
    		cout << *m_Name <<  "     !" << endl;
    	}
    	~Cat()
    	{
         
    		cout << "Cat      !" << endl;
    		if (this->m_Name != NULL) {
         
    			delete m_Name;
    			m_Name = NULL;
    		}
    	}
    
    public:
    	string *m_Name;
    };
    
    void test01()
    {
         
    	Animal *animal = new Cat("Tom");
    	animal->Speak();
    
    	//         ,              ,      
    	//    ?            
    	//                       
    	delete animal;
    }
    
    int main() {
         
    
    	test01();
    
    	system("pause");
    
    	return 0;
    }
    

    まとめ:1.ダミー構造または純ダミー構造は、親ポインタによって子クラスオブジェクトを解放することを解決するために用いる.サブクラスにスタックデータがない場合は、虚析または純虚析と書くことはできない.純粋な虚構造関数を持つクラスも抽象クラスに属する
    4.7.6マルチステートケース3-コンピュータ組立
    ケースの説明:
    コンピュータの主な構成部品はCPU(計算用)、グラフィックスカード(表示用)、メモリバー(記憶用)である.
    各部品を抽象ベースクラスにカプセル化し、IntelメーカーやLenovoメーカーなど、異なるメーカーが異なる部品を生産する
    コンピュータクラスを作成すると、コンピュータを動作させる関数が提供され、各部品を動作させるインタフェースが呼び出されます.
    テスト時に3台の異なるパソコンを組み立てて作業する
    例:
    #include
    using namespace std;
    
    //  CPU 
    class CPU
    {
         
    public:
    	//       
    	virtual void calculate() = 0;
    };
    
    //     
    class VideoCard
    {
         
    public:
    	//       
    	virtual void display() = 0;
    };
    
    //      
    class Memory
    {
         
    public:
    	//       
    	virtual void storage() = 0;
    };
    
    //   
    class Computer
    {
         
    public:
    	Computer(CPU * cpu, VideoCard * vc, Memory * mem)
    	{
         
    		m_cpu = cpu;
    		m_vc = vc;
    		m_mem = mem;
    	}
    
    	//       
    	void work()
    	{
         
    		//       ,    
    		m_cpu->calculate();
    
    		m_vc->display();
    
    		m_mem->storage();
    	}
    
    	//         3     
    	~Computer()
    	{
         
    
    		//  CPU  
    		if (m_cpu != NULL)
    		{
         
    			delete m_cpu;
    			m_cpu = NULL;
    		}
    
    		//      
    		if (m_vc != NULL)
    		{
         
    			delete m_vc;
    			m_vc = NULL;
    		}
    
    		//       
    		if (m_mem != NULL)
    		{
         
    			delete m_mem;
    			m_mem = NULL;
    		}
    	}
    
    private:
    
    	CPU * m_cpu; //CPU     
    	VideoCard * m_vc; //      
    	Memory * m_mem; //       
    };
    
    //    
    //Intel  
    class IntelCPU :public CPU
    {
         
    public:
    	virtual void calculate()
    	{
         
    		cout << "Intel CPU     !" << endl;
    	}
    };
    
    class IntelVideoCard :public VideoCard
    {
         
    public:
    	virtual void display()
    	{
         
    		cout << "Intel        !" << endl;
    	}
    };
    
    class IntelMemory :public Memory
    {
         
    public:
    	virtual void storage()
    	{
         
    		cout << "Intel         !" << endl;
    	}
    };
    
    //Lenovo  
    class LenovoCPU :public CPU
    {
         
    public:
    	virtual void calculate()
    	{
         
    		cout << "Lenovo CPU     !" << endl;
    	}
    };
    
    class LenovoVideoCard :public VideoCard
    {
         
    public:
    	virtual void display()
    	{
         
    		cout << "Lenovo        !" << endl;
    	}
    };
    
    class LenovoMemory :public Memory
    {
         
    public:
    	virtual void storage()
    	{
         
    		cout << "Lenovo         !" << endl;
    	}
    };
    
    
    void test01()
    {
         
    	//       
    	CPU * intelCpu = new IntelCPU;
    	VideoCard * intelCard = new IntelVideoCard;
    	Memory * intelMem = new IntelMemory;
    
    	cout << "         :" << endl;
    	//       
    	Computer * computer1 = new Computer(intelCpu, intelCard, intelMem);
    	computer1->work();
    	delete computer1;
    
    	cout << "-----------------------" << endl;
    	cout << "         :" << endl;
    	//       
    	Computer * computer2 = new Computer(new LenovoCPU, new LenovoVideoCard, new LenovoMemory);;
    	computer2->work();
    	delete computer2;
    
    	cout << "-----------------------" << endl;
    	cout << "         :" << endl;
    	//       
    	Computer * computer3 = new Computer(new LenovoCPU, new IntelVideoCard, new LenovoMemory);;
    	computer3->work();
    	delete computer3;
    
    }