C ++ нарезка вызывает утечку / неопределенное поведение / сбой

Есть ли какой-нибудь пример эффекта нарезки объектов C ++, который может вызвать неопределенное поведение, утечку памяти или сбой в другом правильном наборе кода? Например, когда класс A а также B (наследуется от A) верны и здравы, но зовут void f(A a) явно вызывает неприятные вещи.

Это необходимо для формирования тестового вопроса. Цель состоит в том, чтобы узнать, знает ли участник о явлении нарезки или нет, используя пример фрагмента кода, правильность которого не должна зависеть от мнения.

1

Решение

Если A действительно «правильно и правильно», то срезы (копирование базового подобъекта) четко определены и не вызовут проблем, о которых вы упомянули. Единственная проблема, которую это вызовет, — это непредвиденное поведение, если вы ожидаете, что копия будет вести себя как B,

Если A не правильно копируется, тогда нарезка будет вызывать любые проблемы, возникающие при копировании объектов этого типа. Например, если у него есть деструктор, который удаляет указатель, удерживаемый объектом, и копирование создает новый указатель на одну и ту же вещь, тогда вы получите неопределенное поведение, когда оба деструктора удалят один и тот же указатель. Это не проблема с нарезкой как таковой, но с неверной семантикой копирования нарезанного объекта.

4

Другие решения

Вы всегда можете построить такой пример

struct A {
A() : invariant(true) {}
virtual void do_sth() { assert(invariant); }
protected:
bool invariant;
};

struct B : A {
B() { invariant=false; }
virtual void do_sth() { }
};

void f(A a)
{
a.do_sth();
}

Конечно, это можно предотвратить внутри Aкогда конструктор копирования / оператор присваивания не проверяет, является ли инвариант истинным.

Если инвариант является более неявным, чем мое логическое значение, эти вещи может быть очень сложно увидеть.

1

Нарезка объектов на самом деле является проблемой, только если вы манипулируете производными классами с помощью указателей или ссылок на их базовый класс. Затем дополнительные данные производного класса остаются неизменными, в то время как данные в базовой части могут быть изменены. Это может нарушить инварианты производного класса. Для простого примера см. http://en.wikipedia.org/wiki/Object_slicing

Тем не менее, я бы посчитал это недостаток дизайна (производного класса). Если доступ к классу любым легальным методом, в том числе указателем или аргументом ссылки на базовый класс, может нарушить его инварианты, класс плохо спроектирован. Один из способов избежать этого — объявить базу private, так что производный класс не может быть юридически доступен через его базу.

Примером может быть:

class A
{
int X;
A(int x) : X(x) {}
void doubleX() { X+=X; }
/* ... */
};

class B : public A
{
int X_square;
B(int x) : A(x), X_square(x*x) {}
/* ... */
};

B b(3);
B.doubleX();   /// B.X = 6 but B.X_square=9

Из этого примера также очевидно, что это простой недостаток дизайна в B. В этом примере

void B::doubleX() { A::doubleX(); X_squared=X*X; }

не решает проблему, так как

A&a=b;
a.doubleX();

все еще ломает инвариант. Единственное решение здесь — объявить базу A частный или, лучше, сделать A частный член, а не база, из B,

1
По вопросам рекламы [email protected]