Почему виртуальные функции должны передаваться с указателем, а не по значению (объекта)? - PullRequest
7 голосов
/ 28 апреля 2011

Мне кажется, я понимаю концепцию виртуальных методов и виртуальных таблиц, но не понимаю, почему существует разница между передачей объекта в качестве указателя (или ссылки) и передачей его по значению (какой тип уничтожает виртуальную таблицучто-то?)

Почему бы что-то вроде этой работы:

Material* m = new Texture;
poly->setMaterial(m); 
// methods from Texture are called if I keep carrying the pointer around

А не это?:

Material m = Texture();
poly->setMaterial(m);
// methods from Material are called if I pass the value around

Ответы [ 7 ]

17 голосов
/ 28 апреля 2011

Потому что, если вы передадите по значению, тогда произойдет разрезание объекта , и полиморфизм во время выполнения не может быть достигнут. А в вашем коде сама строка Material m = Texture() вызывает нарезку объектов. Таким образом, даже если вы передадите m по указателю (или ссылке), полиморфизм во время выполнения не будет достигнут.

Кроме того, полиморфизм времени исполнения достигается через:

  • указатель базового типа или
  • ссылка базового типа

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

Material* m1 = new Texture();
poly->setMaterial(m1);     //achieved

Texture* t1= new Texture();
poly->setMaterial(t1);     //achieved

Texture t2;
poly->setMaterial( &t2);   //achieved : notice '&'

Material & m2 =  t2;
poly->setMaterial( &m2 );  //achieved : notice '&'

Material  m3;
poly->setMaterial( &m3 );  //NOT achieved : notice '&'

Только в последней строке вы не достигнете полиморфизма во время выполнения.

4 голосов
/ 28 апреля 2011

Material m = Texture() вызовет конструктор Material::Material(Texture const &), или, если он недоступен, конструктор копирования Material, который создает Material, а не Texture.

НетТаким образом, вы можете построить Texture объект для вас, поэтому этот объект нарезается на объект базового класса.

3 голосов
/ 28 апреля 2011

Виртуальные функции отлично работают в обоих ваших примерах.Они работают точно так, как они должны работать.

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

В вашем первом примере вы создали объект типа Texture.Динамический тип объекта - Texture, поэтому виртуальные вызовы переходят к методам Texture.

Во втором случае вы создаете объект типа Material.Динамический тип объекта - Material, поэтому виртуальные вызовы переходят к методам Material.

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

2 голосов
/ 28 апреля 2011

Поскольку Material m = Texture(); разрезает объект - на данный момент, у вас просто есть Material.

1 голос
/ 28 апреля 2011
class Base
{
    //Members       
};

class Derived1:public Base
{
    //Members
};

int main()
{
    Base obj1;
    Derived1 obj2;

    obj1 = obj2;   //Allowed Since Public Inheritance 
}

Когда obj1 = obj2 копируются в obj1 только те члены производного класса obj2, которые унаследованы от базового класса, остальные члены производного класса обрезаются. Это просто потому, что базовый класс obj1 не знает членов класса Derived. Этот феномен называется Object Slicing.

В вашем случае, когда вы вызываете Material m = Texture(), он содержит только элементы Material и, следовательно, любой вызов функции объекта вызывает функции-члены Material, а не Texture.

1 голос
/ 28 апреля 2011
Material m = Texture();

Создается временный Texture, затем создается Material путем копирования части Material Texture.Это называется нарезкой, и обычно это не то, что вам нужно.

1 голос
/ 28 апреля 2011

После того, как вы присвоите объекту Texture текстуру, он будет разрезан на материал.Поэтому любой вызов объекта m будет отправлять только функции Material.

Material m имеет место только для одного объекта Material.

Используя простые структуры, я проиллюстрирую, что подразумевается под нарезкой:

struct A { 
  int a;
};

struct B : public A {
  int b;
};

A objectA = B();
objectA.b = 1; // compile error, objectA does only have the properties of struct A
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...