Как делать не надо

Модераторы: Hawk, Romeo, Absurd, DeeJayC, WinMain

Аватара пользователя
Romeo
Сообщения: 3126
Зарегистрирован: 02 мар 2004, 17:25
Откуда: Крым, Севастополь
Контактная информация:

somewhere писал(а):Очевидно, копирование будет нормально работать, если поля исходного класса не ссылочные. А если ссылочные, то новый класс и rhs будут содержать поля с одинаковыми ссылками. После уничтожения rhs поля нового класса будут указывать на участки памяти, которые более не доступны.
А если some copy code содержит выделение памяти под такие переменные и копирование данных? Заметь о том, что именно происходит в участке, помеченном, как some copy code ничего не сказано, а значит проблема не может зависеть от того, что там будет написано. В общем, мимо. Ещё варианты? :)
Entites should not be multiplied beyond necessity @ William Occam
---
Для выделения С++ кода используйте конструкцию [ code=cpp ] Код [ /code ] (без пробелов)
---
Сообщение "Спасибо" малоинформативно. Благодарность правильнее высказать, воспользовавшись кнопкой "Reputation" в виде звёздочки, расположенной в левом нижнем углу рамки сообщения.
Аватара пользователя
Оксалайя
Сообщения: 27
Зарегистрирован: 01 сен 2015, 12:12

Рекурсия copy-конструкторов :)
Аватара пользователя
Romeo
Сообщения: 3126
Зарегистрирован: 02 мар 2004, 17:25
Откуда: Крым, Севастополь
Контактная информация:

Оксалайя, хвалю :)
Entites should not be multiplied beyond necessity @ William Occam
---
Для выделения С++ кода используйте конструкцию [ code=cpp ] Код [ /code ] (без пробелов)
---
Сообщение "Спасибо" малоинформативно. Благодарность правильнее высказать, воспользовавшись кнопкой "Reputation" в виде звёздочки, расположенной в левом нижнем углу рамки сообщения.
Аватара пользователя
Romeo
Сообщения: 3126
Зарегистрирован: 02 мар 2004, 17:25
Откуда: Крым, Севастополь
Контактная информация:

Ещё одна зарисовка посложнее. Код выдуман, но суть проблемы тоже взята из реальной жизни:

Код: Выделить всё

class CArray
{
public:
   CArray(int nSize) : 
      m_nSize(nSize)
      m_pArray(new int [nSize])
   {
   }

   CArray& operator=(const CArray& rhs)
   {
      delete [] m_pArray;
      m_pArray = new int [m_nSize = rhs.m_nSize];
      memcpy(m_pArray, rhs.m_pArray, rhs.m_nSize * sizeof(*m_pArray));
      return *this;
   }

   ~CArray()
   {
      delete [] m_pArray;
   }

   // some additional public methods

   static void SetShared(CArray* pArray)
   {
      sm_pSharedArray = pArray;
   }

   static CArray* GetShared()
   {
      return sm_pSharedArray;
   }

private:
   int m_nSize;
   int* m_pArray;

private:
   static CArray* sm_pSharedArray;
};


void main()
{
   CArray arr;
   CArray::SetShared(&arr);

   ...

   CArray* pCurArray = &arr;

   ...

   *(CArray::GetShared()) = *pCurArray;
}
Кто знает, почему запуск приведёт к крашу и как исправить эту проблему?
Entites should not be multiplied beyond necessity @ William Occam
---
Для выделения С++ кода используйте конструкцию [ code=cpp ] Код [ /code ] (без пробелов)
---
Сообщение "Спасибо" малоинформативно. Благодарность правильнее высказать, воспользовавшись кнопкой "Reputation" в виде звёздочки, расположенной в левом нижнем углу рамки сообщения.
Аватара пользователя
Сионист
Сообщения: 1211
Зарегистрирован: 31 мар 2014, 06:18

Оксалайя писал(а):А Вы ожидали где-нибудь увидеть что-то вроде "Не делайте бесконечную рекурсию, потому что получится бесконечная рекурсия"? Так для 99% людей это и так очевидно, Вы просто в эти 99% не попали :)
Ваще то для меня очевидно. Но для всех ли новичков очевидно сразу, что это к бесконечной рекурсии приведёт? О бесконечности рекурсии данных при вложении объект не по указателю в собственный класс (

Код: Выделить всё

class A
{
 A a;
 ....
};
) почему то предупреждают. И отдельно пишут, что та же рекурсия данных на указателях (

Код: Выделить всё

class A
{
 A *a;
 ...
};
) допустима, так как конечна.
Писать можно на чём угодно, но зачем же так себя ограничивать? Пиши на c.
Аватара пользователя
Сионист
Сообщения: 1211
Зарегистрирован: 31 мар 2014, 06:18

Romeo писал(а):А вот третий вариант крайне спорен. Я неоднократно встречал функцию, возвращающую ссылку на локальную статическую переменную, которая делала какие-то дополнительные проверки перед возвратом.

Код: Выделить всё

int &f(...)
{
 static int x;
 ....
 return x;
}
int main ()
{
 int &y=f();
 return 0;
Какие бы проверки ни выполнялись, они будут однократными, а в дальнейшем переменной y можно пользоваться без ограничений. Но если проверка обязательно перед доступом, то должна предварять каждый доступ. Вынесите переменную в отдельный модуль, или вообще заверните её в класс и напишите функции set и get. Другое дело, когда проверке подлежит существование одной из многих безымянных переменных, тогда что бы ни произошло перед повторным доступом, проверяемое условие не изменится, а с ним сохранятся и результаты всех проверок. Да и то не всегда, если в класс завёрнут, например, динамический массив, то элемент может и перестать существовать.
Писать можно на чём угодно, но зачем же так себя ограничивать? Пиши на c.
Аватара пользователя
Сионист
Сообщения: 1211
Зарегистрирован: 31 мар 2014, 06:18

Romeo писал(а):От меня чуть более сложная зарисовка, тоже про конструкторы. Лично встречал такой код в проекте. Кто увидит и опишет суть проблемы будет мною похвален:

Код: Выделить всё

classs CSomeClass
{
public:
   // default constructor
   CSomeClass() 
   {
      // some initialization code
   }

   // copy constructor
   CSomeClass(const CSomeClass rhs)
   {
      // some copy code
   }

private:
   // some private methods and fields

};
Конструктор копирования должен копировать, для чего необходим код, не повторяющий код дефолтного конструктора, так как дефолтный не копирует. Повторять дефолтный конструктор в лучшем случае должна лишь часть кода. Или под some copy code понимается не копия кода, а копирующий код? Да и синтаксис перевран: копирующий конструктор должен принимать константную ссылку, а не просто объект, иначе он получит уже копию, а её то он и должен изготовить.
Писать можно на чём угодно, но зачем же так себя ограничивать? Пиши на c.
Аватара пользователя
Сионист
Сообщения: 1211
Зарегистрирован: 31 мар 2014, 06:18

Romeo писал(а):Ещё одна зарисовка посложнее. Код выдуман, но суть проблемы тоже взята из реальной жизни:

Код: Выделить всё

class CArray
{
public:
   CArray(int nSize) : 
      m_nSize(nSize)
      m_pArray(new int [nSize])
   {
   }

   CArray& operator=(const CArray& rhs)
   {
      delete [] m_pArray;
      m_pArray = new int [m_nSize = rhs.m_nSize];
      memcpy(m_pArray, rhs.m_pArray, rhs.m_nSize * sizeof(*m_pArray));
      return *this;
   }

   ~CArray()
   {
      delete [] m_pArray;
   }

   // some additional public methods

   static void SetShared(CArray* pArray)
   {
      sm_pSharedArray = pArray;
   }

   static CArray* GetShared()
   {
      return sm_pSharedArray;
   }

private:
   int m_nSize;
   int* m_pArray;

private:
   static CArray* sm_pSharedArray;
};


void main()
{
   CArray arr;
   CArray::SetShared(&arr);

   ...

   CArray* pCurArray = &arr;

   ...

   *(CArray::GetShared()) = *pCurArray;
}
Кто знает, почему запуск приведёт к крашу и как исправить эту проблему?
Не обновляется размер массива. И строка *(CArray::GetShared()) = *pCurArray; для такого прототипа GetShared какая то странная. Она вообще скомпилируется?
Писать можно на чём угодно, но зачем же так себя ограничивать? Пиши на c.
Аватара пользователя
Сионист
Сообщения: 1211
Зарегистрирован: 31 мар 2014, 06:18

Если в класс завёрнут динамический массив, то не следует возвращать просто ссылку на элемент, так как:
1. Она может быть присвоена переменной-ссылке (

Код: Выделить всё

int &x=array[200];
).
2. Её адрес может быть присвоен переменной-указателю (

Код: Выделить всё

int *p=&(array[200]);
.
и после этого может быть изменён размер массива, в результате чего может:
1. Измениться фактическое размещение элементов.
2. Количество элементов может стать меньше или равно индексу, после чего элемент с таким индексом может перестать существовать.
Вместо этого следует написать вспомогательный private-класс-член, конструктор которого будет принимать ссылку на базовый тип массива и присваивать адрес этого параметра private-указателю-члену, типом которого должен быть указатель на базовый тип пассива. Этот вспомогательный класс должен иметь оператор-член привидения к базовому типу массива и оператор присваивания константной ссылки на базовый тип массива. Вот объект этого класса и должен возвращать оператор квадратные скобки: оператор квадратные скобки можно будет применять в качестве и правого, и левого оператор присваивания, но нельзя будет взять адрес возвращаемого им значения:

Код: Выделить всё

class TIntArray
{
 private:
  int *Data;
  class IntProperty
  {
   private:
    int *Point;
   public:
    IntProperty (int &n)
    {
     Point=&n;
    }
    operator int ()
    {
     return *Point;
    }
    IntProperty operator = (const int &x)
    {
     *Point=x;
    }
   };
   public:
  IntProperty operator [] (size_t i)
  {
   ...
   return IntProperty(Data[i]);
  }
...
};
. Здесь условно не показаны конструкторы и деструкторы класса TIntArray, копирование и присваивание его экземпляров, проверка индекса и функции, меняющие, или возвращающие количество элементов. Не пытайтесь закрыть или удалить конструктор копий вспомогательного класса, даже если оператор квадратные скобки возвращает ссылку на экземпляр вспомогательного класса: не скомпилируется.
Писать можно на чём угодно, но зачем же так себя ограничивать? Пиши на c.
Аватара пользователя
Romeo
Сообщения: 3126
Зарегистрирован: 02 мар 2004, 17:25
Откуда: Крым, Севастополь
Контактная информация:

Сионист писал(а):

Код: Выделить всё

int &f(...)
{
 static int x;
 ....
 return x;
}
int main ()
{
 int &y=f();
 return 0;
Какие бы проверки ни выполнялись, они будут однократными, а в дальнейшем переменной y можно пользоваться без ограничений.
Неверно. Код, который в твоём примере скрыт в трёх точках будет выполняться КАЖДЫЙ раз при вызове f.

Ещё раз подчёркиваю, что подобный подход не сомнителен, а, напротив, более чем нормален и читабелен. Но при условии, что код в трёх точках не пустой.
Entites should not be multiplied beyond necessity @ William Occam
---
Для выделения С++ кода используйте конструкцию [ code=cpp ] Код [ /code ] (без пробелов)
---
Сообщение "Спасибо" малоинформативно. Благодарность правильнее высказать, воспользовавшись кнопкой "Reputation" в виде звёздочки, расположенной в левом нижнем углу рамки сообщения.
Ответить