OOP(オブジェクト指向プログラミング)に関する質問 - ページ 10

 

パラメータ名は重要ではありません...異なる名前は、何かが何かと混同されないようにするために意味があります...。

関数宣言の中にいくつかの値を書くことができます。

public:
   void              SetName(string n);

といった値を関数自体に設定します。

void CPerson::SetName(string nnn)
  {
   m_name.first_name=GetFirstName(nnn);
   m_name.last_name=GetLastName(nnn);
  }

あるいは,どこでも同じようなパラメータ名をつけることができ,どちらか書き手にとって便利な方を選びます

 

同じ教科書の中で、そのコードは出会ったのです。

#property copyright "Copyright 2011, MetaQuotes Software Corp."
#property link      "https://www.mql5.com"
#property version   "1.00"
//+------------------------------------------------------------------+
//| Класс-пример с несколькими типами доступа                        |
//+------------------------------------------------------------------+
class CBaseClass
  {
private:             //--- закрытый член недоступен из потомков
   int               m_member;
protected:           //--- защищенный метод доступен из базового класса и его потомков
   int               Member(){return(m_member);}
public:              //--- конструктор класса доступен всем
                     CBaseClass(){m_member=5;return;};
private:             //--- закрытый метод для присвоения значения члену m_member
   void              Member(int value) { m_member=value;};
  };
//+------------------------------------------------------------------+
//| Производный класс с ошибками                                     |
//+------------------------------------------------------------------+
class CDerived: public CBaseClass // public наследование можно не указывать, оно по умолчанию
  {
public:
   void Func() // определим в потомке функцию с обращениями к членам базового класса 
     {
      //--- попытка модификации закрытого члена базового класса
      m_member=0;        // ошибка, закрытый член базового класса никому не доступен
      Member(0);         // ошибка, закрытый метод базового класса не доступен в потомках

コンストラクタの奇妙な点

public:              //--- конструктор класса доступен всем
                     CBaseClass(){m_member=5;return;};
なぜここにリターン 演算子があるのですか?

この演算子がコンストラクタで使われているのを見たのは初めてです。実際には、コンストラクタは自動的に呼び出されます。そして、とにかく出力があること。この演算子はコンストラクタの中で意味を持つのでしょうか?

 
hoz:

同じ教科書の中で、コードが流れてきた。

コンストラクタの奇妙な点

なぜここにリターン 演算子があるのですか?

この演算子がコンストラクタで使われているのを見たのは初めてです。実際には、コンストラクタは自動的に呼び出されます。そして、とにかくアウトプットがあることでしょう。この演算子はコンストラクタの中で意味を持つのでしょうか?

この例では必要ありませんが、早期終了が必要な場合、複雑な初期化が必要になることがあります。

コンストラクタとデストラクタは通常の関数です。デフォルトのコンストラクタとデストラクタのみが自動的に呼び出されます。その他はユーザーから呼び出される。

 

教科書では、ポリモーフィズムとの関連でこのような例が挙げられている。

//--- Базовый класс
class CShape
  {
protected: 
   int            m_type;                // тип фигуры
   int            m_xpos;                // X - координата точки привязки
   int            m_ypos;                // Y - координата точки привязки
public:
   void           CShape(){m_type=0;};   // конструктор, тип равен нулю
   int            GetType(){return(m_type);};// возвращает тип фигуры
virtual
   double         GetArea(){return (0); }// возвращает площадь фигуры
  };
//--- производный класс Круг
class CCircle : public CShape            // после двоеточия указывается базовый класс,
  {                                      // от которого производится наследование 
private:
   double         m_radius;              // радиус круга
public:
   void           CCircle(){m_type=1;};  // конструктор, тип равен 1 
   void           SetRadius(double r){m_radius=r;};
   virtual double GetArea(){return (3.14*m_radius*m_radius);}// площадь круга
  };
class CSquare : public CShape            // после двоеточия указывается базовый класс,
  {                                      // от которого производится наследование 
private:
   double          m_square_side;        // сторона квадрата
public:
   void            CSquare(){m_type=2;}; // конструктор, тип равен 2 
   void            SetSide(double s){m_square_side=s;};
   virtual double  GetArea(){return (m_square_side*m_square_side);}//площадь квадрата
  };

ひとつだけわからないことがあるんです。子関数オブジェクト、すなわち派生メソッドCCircleと CSquareを 呼び出しに使えば、ベースクラスでの宣言をバイパスしてGetArea() 面積を計算することができます。つまり、ベースクラスには仮想関数を 一切作らず、派生メソッドには単純なメソッドを作る、それだけです。では、なぜ仮想関数が必要なのでしょうか。

適切かつ論理的な例で、仮想関数が何らかの利益をもたらすことがわかるのは興味深いことです。なぜなら、私が見たものは、少なくとも私にとっては論理的ではなかったからです。同じように理解したいと思います。

 
hoz:

教科書では、ポリモーフィズムとの関連でこのような例が挙げられている。

ひとつだけわからないことがあるんです。子関数オブジェクト、すなわち派生メソッドCCircleと CSquareを 呼び出しに使えば、ベースクラスでの宣言をバイパスしてGetArea() 面積を計算することができます。つまり、ベースクラスでは仮想関数を一切作らず、派生メソッドでは単純なメソッドを作る、それだけです。では、なぜ仮想関数が必要なのでしょうか?

適切かつ論理的な例で、仮想関数が何らかの利益をもたらすことがわかるのは興味深いことです。なぜなら、私が見たものは、少なくとも私にとっては論理的ではなかったからです。同じように理解したいと思います。

これは多相性を理解するための最もシンプルなサンプルです。早く手に入れるために。

複雑なケースもある。必要な時に必要な分だけ塗る。今さら悩んでも仕方がない。タスクが終わると、考えることがあります。

例えば、可能な限りの読み取り/書き込みインターフェイスを持つベースクラスがあるとします。また、プライベート仮想メソッド(読み書き計2個)を持ち、ベースクラス内のこのインターフェイスと派生クラスとを結びつけて います。実際には、派生クラスはファイル(ファイル、マッピング、チャンネル、インターネット)を扱うものであれば何でもかまいません。派生クラスはそれぞれ異なる仮想メソッドを定義していますが、すべてのクラスがベースクラスから同じインタフェースを有しています。

 
hoz:

教科書では、ポリモーフィズムとの関連でこのような例が挙げられている。

ひとつだけわからないことがあるんです。子関数オブジェクト、すなわち派生メソッドCCircleと CSquareを 呼び出しに使えば、ベースクラスでの宣言をバイパスしてGetArea() 面積を計算することができます。つまり、ベースクラスでは仮想関数を一切作らず、派生メソッドでは単純なメソッドを作る、それだけです。では、なぜ仮想関数が必要なのでしょうか?

適切かつ論理的な例で、仮想関数が何らかの利益をもたらすことがわかるのは興味深いことです。なぜなら、私が見たものは、少なくとも私にとっては論理的ではなかったからです。理解したいのは同じです。

小さなサンプルをスケッチしてみる。

#property strict
#property show_inputs

enum Mes_type {
    m1,
    m2
};
input Mes_type mes_t;  // Выберите тип сообщения

class Message {
public:
    virtual void action() {};
};

class Mes1 : public Message {
public:
    virtual void action() {Alert("Типичные ошибки в программах");}
};

class Mes2 : public Message {
public:
    virtual void action() {Alert("Оффлайновые графики");}
};

void OnStart() {
    // Формируем входные данные для какого-то алгоритма
    //////////////////////////////////////////
    Message *mes;                           //
    switch(mes_t)                           //
    {                                       //
        case m1:                            //
            mes = new Mes1;                 //
            break;                          //
        case m2:                            //
            mes = new Mes2;                 //
    }                                       //
    /////////////////////////////////////////
    
    // Рабочий алгоритм
    //////////////////////////////////////////
    mes.action();                           //
    //////////////////////////////////////////
  
    delete mes;
}

この構造のおかげで、非常に大きく複雑になる可能性のあるアルゴリズムを扱う必要はなく(ここではすべて簡略化しています)、列挙でm3という子孫を1つ追加し、スイッチでケースを1つ追加するだけでよいのです。つまり、入力データを統一することで、プログラム本編での編集を回避しているのです。

もちろん、これは作業アルゴリズムが様々なタイプを入力として受け入れる場合にのみ適切である。1種類しかなければ、すべて意味がない。

 
hoz:

教科書では、ポリモーフィズムとの関連でこのような例が挙げられている。

ひとつだけわからないことがあるんです。子関数オブジェクト、すなわち派生メソッドCCircleと CSquareを 呼び出しに使えば、ベースクラスでの宣言をバイパスしてGetArea() 面積を計算することができます。つまり、ベースクラスでは仮想関数を一切作らず、派生メソッドでは単純なメソッドを作る、それだけです。では、なぜ仮想関数が必要なのでしょうか?

適切かつ論理的な例で、仮想関数が何らかの利益をもたらすことがわかるのは興味深いことです。なぜなら、私が見たものは、少なくとも私にとっては論理的ではなかったからです。同じように理解したいと思います。

ここで簡単な例を挙げてみましょう。

CShape* GetNewShape()
{
        if( ... ) // Здесь какое-то условие.
                return new CCircle();
        else
                return new CSquare();
}

CShape* M[10];

for( int i = 0; i < 10; i++ )
{
        M[i] = GetNewShape();
}


double WholeArea = 0.0;
for( int i = 0; i < 10; i++ )
{
        WholeArea += M[i].GetArea();
}
GetArea()関数がどの図形に対して呼び出されたかを知らずに使用しています。
 

このセッターをクラスで持っています。

//---- SetColorBySend
TradingFunc::SetColorBySend (const color fc_ColorSendBuy,      // Цвет открытия ордера на покупку
                             const color fc_ColorSendSell)     // Цвет открытия ордера на продажу
{
   ColorBySend [2] = {fc_ColorSendBuy, fc_ColorSendSell};
}

コンパイラは一般にこのColorBySend 配列への要素の 割り当てをこのように扱っています。

'fc_ColorSendBuy' - constant expression required        TradingFunc.mqh 91      23
'fc_ColorSendSell' - constant expression required       TradingFunc.mqh 91      40
'{' - expression expected       TradingFunc.mqh 91      22
これと何の関係があるのですか?本当に要素ごとに値を割り当てる必要があるのでしょうか?リストとして行うことはできないのでしょうか?何に関連しているのか?何しろ、教科書でも課題がそうなっているのですから...。
 
hoz:

このセッターをクラスで持っています。

コンパイラはこのようにColorBySend 配列への要素の割り当てを一般的に悪用しています。

これと何の関係があるのですか?本当に要素ごとに値を割り当てる必要があるのでしょうか?リストとして行うことはできないのでしょうか?それと何の関係があるのですか?何しろ、教科書でも課題はこうなっているのですから...。


何か、何か他のもの}の種類の構成要素は、定数の配列にしかできません。しかし、関数に渡されたパラメータは、実際には関数のローカル 変数であるそこに代入されます。この場合の修飾子constは、渡された値が変更できないことを示すだけで、意味はない。その結果、
{fc_ColorSendBuy, fc_ColorSendSell}
、コンパイラが理解できない変数式になっています。嗚呼。
 
リストによる初期化は、宣言がある場合のみ可能である。
理由: