exponenta event banner

Неправильное объектно-ориентированное программирование

Динамический тип this неверный указатель

Описание

Эта проверка вызова функции члена класса определяет, является ли вызов допустимым.

Вызов функции члена может быть недействительным по следующим причинам:

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

  • Вы называете чистый virtual функция-член из конструктора класса или деструктора.

  • Вы называете virtual функция-член через неверную this указатель. this указатель сохраняет адрес объекта, используемого для вызова функции. this указатель может быть неверным по следующей причине:

    • Объект можно получить с помощью литейной формы из другого объекта. Объекты являются экземплярами двух несвязанных классов.

    • Для указателя, указывающего на массив объектов, выполняется арифметика указателя. Однако арифметика указателя приводит к выходу указателя за пределы массива. При отмене привязки указателя он не указывает на допустимый объект.

Примеры

развернуть все

#include <iostream>
class myClass {
public: 
  void method() {}
};

void main() {
  myClass Obj;
  int (myClass::*methodPtr) (void) = (int (myClass::*) (void)) &myClass::method;
  int res = (Obj.*methodPtr)();
  std::cout << "Result = " << res;
}

В этом примере указатель methodPtr имеет тип возврата int но указывает на myClass:method с возвращаемым типом void. Следовательно, когда methodPtr отличается, проверка Неверное объектно-ориентированное программирование приводит к красной ошибке.

#include <iostream>
class myClass {
public:
  void method() {}
};

void main() {
  myClass Obj;
  void (myClass::*methodPtr) (void) =  &myClass::method;
  methodPtr = 0;
  (Obj.*methodPtr)();
}

В этом примере: methodPtr имеет значение NULL когда он обособлен.

class Shape {
public:
  Shape(Shape *myShape) {
    myShape->setShapeDimensions(0.0);
  }
  virtual void setShapeDimensions(double) = 0;
};

class Square: public Shape {
  double side;
public:
  Square():Shape(this) {
  }
  void setShapeDimensions(double);
};

void Square::setShapeDimensions(double val) {
  side=val;
}

void main() {
  Square sq;
  sq.setShapeDimensions(1.0);
}

В этом примере производный конструктор класса Square::Square вызывает конструктор базового класса Shape::Shape() с его this указатель. Затем конструктор базового класса вызывает чистую виртуальную функцию Shape::setShapeDimensions через this указатель. Поскольку вызов чистой виртуальной функции из конструктора не определен, проверка Неверное объектно-ориентированное программирование приводит к красной ошибке.

#include <new>

class Foo {
public:
  void funcFoo() {}
};


class Bar {
public:
  virtual void funcBar() {}
};

void main() {
  Foo *FooPtr = new Foo;
  Bar *BarPtr = (Bar*)(void*)FooPtr;
  BarPtr->funcBar();
}

В этом примере классы Foo и Bar не связаны. Когда Foo* указатель приводится к Bar* указатель и Bar* указатель используется для вызова virtual функция-член класса Bar, проверка Неверное объектно-ориентированное программирование приводит к красной ошибке.

#include <new>
class Foo {
public:
    virtual void func() {}
};

void main() {
    Foo *FooPtr = new Foo[4];
    for(int i=0; i<=4; i++)
        FooPtr++;
    FooPtr->func();
    delete [] FooPtr;
}

В этом примере указатель FooPtr точки за пределами выделенных границ, когда он используется для вызова virtual функция-член func(). Он не указывает на допустимый объект. Поэтому проверка Неверное объектно-ориентированное программирование приводит к красной ошибке.

class Foo {
public:
  virtual int func() {
    return 1;
  }
};

class Ref {
public:
  Ref(Foo* foo) {
    foo->func();
  }
};

class Bar {
private:
  Ref m_ref;
  Foo m_Foo;
public:
  Bar() : m_ref(&m_Foo) {}
};

В этом примере конструктор Bar::Bar() вызывает конструктор Ref::Ref() с адресом m_Foo прежде m_Foo инициализирован. Когда virtual функция-член func вызывается через указатель, указывающий на &m_Foo, проверка Неверное объектно-ориентированное программирование приводит к красной ошибке.

Для воспроизведения результатов проанализируйте только класс Bar использование опции Class (-class-analyzer).

#include <new>

class Foo {
public:
  virtual void funcFoo() {}
};


class Bar: public Foo {
public:
  void funcFoo() {}
};

void main() {
  Foo *FooPtr = new Foo;
  Bar *BarPtr = (Bar*)(void*)FooPtr;
  BarPtr->funcFoo();
}

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

Указатель FooPtr указывает на Foo объект. Приведение неправильно пытается преобразовать Foo* указатель FooPtr в Bar* указатель BarPtr. BarPtr по-прежнему указывает на основание Foo объект и не может получить доступ Bar::funcFoo.

Исправление - Сделать точку указателя базового класса непосредственно на производный объект класса

Полиморфизм C++ позволяет определить указатель, который может пересекать иерархию класса для указания на наиболее производную функцию-член. Для правильной реализации полиморфизма следует начать с указателя базового класса и указать на производный объект класса.

#include <new>

class Foo {
public:
  virtual void funcFoo() {}
};


class Bar: public Foo {
public:
  void funcFoo() {}
};

void main() {
  Foo *FooPtr = new Bar;
  FooPtr->funcFoo();
}

Проверить информацию

Группа: C++
Язык: C++
Акроним: ООП