Почему memcpy приводит копирование к сбою члену локального массива простого объекта?

Я сказал бы, что, если у Вас есть кодовая база, с которой Вы желаете сделать это, это не лучшая разработанная кодовая база. Это - обычно знак класса на одном уровне иерархии, нуждающейся в определенной общедоступной подписи, в то время как другому классу, полученному из того класса, не нужен он.

предстоящую парадигму кодирования называют "Составом по Наследованию". Это играет непосредственно прочь принципов объектно-ориентированной разработки (особенно Единственный Принцип Ответственности, и Откройтесь/Закройте Principle).

, К сожалению, путь многие из нас разработчикам преподавали объектную ориентацию, мы сформировали привычку к непосредственному размышлению о наследовании вместо состава. Мы склонны иметь большие классы, которые имеют много различных обязанностей просто, потому что они могли бы содержаться с тем же объектом "Реального мира". Это может привести к иерархиям классов, которые равняются 5 + уровни глубоко.

неудачный побочный эффект, что разработчики обычно не думают о том, когда контакт с наследованием состоит в том, что наследование формирует одну из самых сильных форм зависимостей, которые можно когда-либо вводить в код. Ваш производный класс является теперь сильно иждивенцем на классе, от которого он был наследован. Это может сделать Ваш код более хрупким в конечном счете и привести к соединению проблем, где изменение определенного поведения в базовом классе повреждает производные классы неясными способами.

Один способ разбить Ваш код через интерфейсы, любят упомянутый в другом ответе. Это - умная вещь сделать так или иначе, поскольку Вы хотите, чтобы внешние зависимости класса связали с абстракциями, не бетоном/производными типами. Это позволяет Вам изменять реализацию, не изменяя интерфейс, все, не производя строку кода в Вашем зависимом классе.

я был бы очень, а не обслуживать систему с сотнями/тысячами/ровными большего количества классов, которые являются все маленькими и слабо связываются, чем соглашение с системой, которая делает интенсивное использование полиморфизма/наследования и имеет меньше классов, которые более сильно связаны.

, Возможно, лучший ресурс там на объектно-ориентированной разработке является книгой Robert C. Martin, Гибкая разработка программного обеспечения, Принципы, Шаблоны и Методы .

6
задан Andy Dent 15 October 2009 в 08:17
поделиться

3 ответа

Это, вероятно, хороший пример того, почему (на мой взгляд) плохая идея использовать typedef типы массивов.

В отличие от других контекстов, в объявлениях функций a параметр типа массива всегда настраивается на эквивалентный тип указателя. Когда массив передается в функцию, он всегда распадается на указатель на первый элемент.

Эти два фрагмента эквивалентны:

typedef unsigned char Str[257];
Str src = "blah";
Str dst;
memcpy( &dst, &src, sizeof(Str) ); // unconventional

unsigned char src[257] = "blah";
unsigned char dst[257];
memcpy(&dst, &src, sizeof(unsigned char[257])); // unconventional

В последнем случае & dst и & src оба имеют тип unsigned char (*) [257] , но значение этих указателей такое же, как и значение указателей на первый элемент каждого массива, что и составляет dst и src распадался бы, если бы он был передан непосредственно в memcpy вот так.

memcpy(dst, src, sizeof(unsigned char[257])); // more usual

memcpy принимает void * аргументов, поэтому типы исходных указателей не имеют значения, только их значения.

Из-за правила для объявления параметров (тип массива любой или неуказанный размер корректируется до эквивалентного типа указателя), все эти объявления для fn эквивалентны:

typedef unsigned char Str[257];
void fn( Str dst, Str src );

void fn( unsigned char dst[257], unsigned char src[257] );

void fn( unsigned char dst[], unsigned char src[] );

void fn( unsigned char* dst, unsigned char* src );

Глядя на этот код, более очевидно, что значения, передаваемые в memcpy в данном случае это указатели на переданные указатели, а не указатели на фактические массивы unsigned char .

// Incorrect
void fn( unsigned char* dst, unsigned char* src )
{
    memcpy(&dst, &src, sizeof(unsigned char[257]));
}

С typedef ошибка не так очевидна, но все же присутствует.

// Still incorrect
typedef unsigned char Str[257];
void fn( Str dst, Str src )
{
    memcpy(&dst, &src, sizeof(Str));
}
7
ответ дан 10 December 2019 в 02:50
поделиться

Если я читаю правильно (а мой C ++ немного заржавел), ваш класс никогда не выделяет место для переменной mStr. Вы объявляете его (но, кажется, не выделяете его) в закрытом разделе, и вы инициализируете первый элемент равным 0 в конструкторе, но вы не видите, что каждый фактически создает объект Str255.

Вам может понадобиться чтобы заменить частное объявление на Str255 mStr () , или вам может потребоваться что-то сделать в конструкторе, например mStr = new Str255 ()

-3
ответ дан 10 December 2019 в 02:50
поделиться

Вы должны написать memcpy (mStr255, s, sizeof (Str255)); . Без "&". Str255 уже является указателем. Это соответствует стандарту C ++ 4.2:

lvalue или rvalue типа «массив NT» или «массив неизвестной границы T» может быть преобразован в rvalue типа «указатель на T.» Результатом является указатель на первый элемент массива.

Почему он где-то работает? Есть два разных указателя (для mStr255 и & mStr255 ), и они имеют разные типы - unsigned char * и unsigned char (*) [257] . Адрес массива такой же, как и адрес первый элемент в массиве, но когда вы передадите его в качестве аргумента функции, вы получите адрес переменной в стеке. Набрав Str255 вы скрываете разницу. Проверьте следующий пример:

unsigned char Blah[10] = "\004Blah";

struct X
{
    void f1( unsigned char(&a)[10] ) // first case (1)
    {
      void* x1 = &a; // pointer to array of unsigned char
      void* x2 = a;  // pointer to unsigned char due to implicit conversion array-to-pointer
    }
    void f2( unsigned char* a )     // second case (2)
    {
      void* x1 = &a; // pointer to variable 'a' which is on the stack
      void* x2 = a;  // pointer to unsigned char
    }
    unsigned char x[10];
};

int main( int argc, char ** argv )
{
    X m;
    m.f1( Blah ); // pass by reference
    m.f2( Blah ); // implicit array-to-pointer conversion

    return 0;
}

Когда вы пишете void f (Str255 a) , он равен второму случаю.

5
ответ дан 10 December 2019 в 02:50
поделиться
Другие вопросы по тегам:

Похожие вопросы: