关于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);}//площадь квадрата
  };

有一件事我不明白。如果我们使用子函数对象进行调用,即派生方法CCircleCSquare,那么GetArea() 面积就可以绕过基类中的声明进行计算。也就是说,根本不要在基类中创建虚拟函数,而在派生方法中创建一个简单的方法,仅此而已。那么,为什么我们需要一个虚拟函数呢?

看到一个充分的、符合逻辑的例子很有意思,你可以看到虚拟函数提供了一些好处。因为我看到的是不符合逻辑的,至少对我来说是这样。不过,我还是想了解一下。

 
hoz:

教科书中给出了这个与多态性有关的例子。

有一件事我不明白。如果我们使用子函数对象进行调用,即派生方法CCircleCSquare,那么GetArea() 面积就可以绕过基类中的声明进行计算。也就是说,根本不要在基类中创建虚拟函数,而在派生方法中创建一个简单的方法,仅此而已。那么,为什么我们需要一个虚拟函数呢?

看到一个充分的、符合逻辑的例子很有意思,你可以看到虚拟函数提供了一些好处。因为我看到的是不符合逻辑的,至少对我来说是这样。我也想了解它。

这是理解多态性的最简单样本。为了迅速得到它。

有一些复杂的案件。你会在你需要的时候应用它。现在打扰是没有意义的。当任务完成后,你就得考虑一下了。

例如,我有一个基类,有所有可能的读/写接口。它也有私有的虚拟方法(共2个--读/写),将基类中的这个接口与派生类 联系起来。实际上,派生类可以是任何与文件有关的工作(文件、映射、通道、互联网)。每个派生类对这些虚拟方法的定义都不同,但所有的类都有与基类相同的接口。

 
hoz:

教科书中给出了这个与多态性有关的例子。

有一件事我不明白。如果我们使用子函数对象进行调用,即派生方法CCircleCSquare,那么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,在开关中增加一个案例。也就是说,我们已经统一了输入数据,这将避免在程序的主要部分进行编辑。

当然,这只有在工作算法接受各种类型的输入时才会合适。如果只有一种类型,所有这些都是无用的。

 
hoz:

教科书中给出了这个与多态性有关的例子。

有一件事我不明白。如果我们使用子函数对象进行调用,即派生方法CCircleCSquare,那么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}
是一个编译器无法理解的变量表达式。唉。
 
通过列表进行初始化只有通过声明才能实现。