C++ несколько виртуальное наследование по сравнению с COM

Каждый раз, когда вы вызываете setValue() или updateChildren() в качестве ссылки, это приводит к записи данных в базу данных. Это означает, что в вашем коде новый узел для нового пользователя создается после следующих операторов:

final DatabaseReference newPost3 = mDatabase3.push();

newPost3.child("userid").setValue(acct.getEmail());

Таким образом, в этот момент база данных содержит только userid для нового узла. Поскольку узел теперь создан, ваша облачная функция сработает, и на этом этапе username и score будут неопределенными.

Чтобы этого не происходило, вам нужно записать весь узел для нового пользователя за один вызов в базу данных. Простой способ сделать это - поместить все данные в HashMap:

Map<String, Object> values = new HashMap<String, Object>();

values.put("userid", acct.getEmail());
values.put("desc", finalValue);
values.put("username", acct.getDisplayName());

mDatabase3.push().setValue(values);
6
задан Adam 18 November 2008 в 22:19
поделиться

5 ответов

Виртуальное наследование в первом примере ничего не делает. Я держал бы пари, что они компилируют в тот же код, если они были удалены.

Фактически наследованный класс просто отмечает компилятор, из которого он должен объединить более поздние версии Der1 или Der2. Так как только один из каждого появляется в дереве наследования, ничто не сделано. virtuals не имеют никакого эффекта на Base.

auto p = new Join2;
static_cast<Base*>(static_cast<Der1*>(p)) !=
      static_cast<Base*>(static_cast<Der2*>(p))

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

class A {};
class B : virtual public A {};
class C : virtual public A {};
class D : public A {};
class E : virtual public A, public B, public C, public D {};
class F : public A, public B, public C, public D {};

F::A != F::B::A or F::C::A or F::D::A
F::B::A == F::C::A
F::D::A != F::B::A or F::C::A or F::A

E::B::A == E::C::A == E::A
E::D::A != E::B::A or E::C::A or E::D::A

Одна из причин, которые Необходимость быть отмеченной виртуальная в C и B вместо E или F - то, что C и B должны знать для не вызова конструктора A. Обычно они имели бы, инициализируют каждую из их копий. Когда они вовлечены в ромбовидное наследование они привычка. Но Вы наклоняетесь, перекомпилировали B и C для не построения A. Это означает C, и B должны знать заранее для создания кода конструктора, где конструктора A не вызывают.

3
ответ дан 17 December 2019 в 04:53
поделиться

Это немного датировано теперь, но лучшей ссылкой, с которой я когда-либо сталкивался, который касается внутренностей C++, является Lippman В Модели Объекта C++. Точные детали реализации не могут соответствовать выводу Вашего компилятора, но пониманию, что он обеспечивает, чрезвычайно ценно.

Вокруг страницы 96 существует объяснение виртуального наследования, и это конкретно решает ромбовидную проблему.

Я оставлю Вас для чтения деталей, но в основном использование виртуального наследования требует поиска в виртуальной таблице для определения местоположения базового класса. Дело обстоит не так в нормальном наследовании, где местоположение базового класса может быть вычислено во время компиляции.

(В прошлый раз, когда я вынул простой способ и просто рекомендовал книге ответить на вопрос переполнения стека, за меня проголосовали значительно, поэтому давайте посмотрим, происходит ли это снова... :)

0
ответ дан 17 December 2019 в 04:53
поделиться

Я думаю, что проблема с Вашим примером COM - то, что путем добавления виртуального ключевого слова Вы говорите, что все IOle* интерфейсы совместно используют общую реализацию IUnknown. Для реализации этого, компилятор должен создать несколько v-таблиц, следовательно Вы отличающийся, 'это' оценивает в зависимости от производного класса, он снизился.

COM требует что при вызове IQueryInterface на объекте для IUnknown, что ВСЕ интерфейсы, выставленные объектом, возвращают тот же IUnknown..., который ясно повреждает эта реализация.

Без виртуального наследования каждый IOle* номинально имеет свою собственную реализацию IUnknown. Однако, так как IUnknown является абстрактным классом и не имеет никакого устройства хранения данных компилятором, и все реализации IUnknown прибывают из FlashContainerWnd существует только единственная реализация.

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

2
ответ дан 17 December 2019 в 04:53
поделиться

Как говорит Каспин, ваш первый пример на самом деле не дает ничего полезного. Однако он будет добавлять vpointer, чтобы указать производным классам, где найти классы, от которых он унаследован.

Это исправляет любые ромбы, которые вы можете теперь создавать (а вы этого не делаете), но поскольку структура классов теперь не больше static, вы больше не можете использовать static_cast на нем. Я не знаком с задействованным API, но то, что Роб Уокер говорит об IUnkown, может быть связано с этим.

Короче говоря, нужно использовать нормальное наследование, когда вам нужен ваш собственный базовый класс , который не должен быть совместно используемыми с родственными классами: (a - контейнер, b, c, d - части, каждая из которых имеет контейнер, e объединяет эти части (плохой пример, почему бы не использовать композицию?))

a  a  a
|  |  |
b  c  d <-- b, c and d inherit a normally
 \ | /
   e

Виртуальное наследование предназначено для случаев, когда ваш базовый класс должен использоваться совместно с ними. (a - транспортное средство, b, c, d - разные специализации транспортного средства, e объединяет их)

   a
 / | \
b  c  d <-- b, c and d inherit a virtually
 \ | /
   d
0
ответ дан 17 December 2019 в 04:53
поделиться

Я решил попробовать ваш пример. Я придумал:

#include "stdafx.h"
#include <stdio.h>

class Base
{
public:
  virtual void say_hi(const char* s)=0;
};

class Der1 : public Base
{
public:
  virtual void d1()=0;
};

class Der2 : public Base
{
public:
  virtual void d2()=0;
};

class Join : virtual public Der1, virtual public Der2
             // class Join : public Der1, public Der2
{
public:
  virtual void say_hi(const char* s);
  virtual void d1();
  virtual void d2();
};

class Join2 : public Join
{
  virtual void d1();
};

void Join::say_hi(const char* s)
{
  printf("Hi %s (%p)\n", s, this);
}

void Join::d1()
{}

void Join::d2()
{}

void Join2::d1()
{
}

int _tmain(int argc, _TCHAR* argv[])
{
  Join2* j2 = new Join2();
  Join* j = dynamic_cast<Join*>(j2);
  Der1* d1 = dynamic_cast<Der1*>(j2);
  Der2* d2 = dynamic_cast<Der2*>(j2);
  Base* b1 = dynamic_cast<Base*>(d1);
  Base* b2 = dynamic_cast<Base*>(d2);

  printf("j2: %p\n", j2);
  printf("j:  %p\n", j);
  printf("d1: %p\n", d1);
  printf("d2: %p\n", d2);
  printf("b1: %p\n", b1);
  printf("b2: %p\n", b2);

  j2->say_hi("j2");
  j->say_hi(" j");
  d1->say_hi("d1");
  d2->say_hi("d2");
  b1->say_hi("b1");
  b2->say_hi("b2");

  return 0;
}

Он дает следующий результат:

j2: 00376C10
j:  00376C10
d1: 00376C14
d2: 00376C18
b1: 00376C14
b2: 00376C18
Hi j2 (00376C10)
Hi  j (00376C10)
Hi d1 (00376C10)
Hi d2 (00376C10)
Hi b1 (00376C10)
Hi b2 (00376C10)

Итак, при приведении Join2 к его базовым классам вы можете получить разные указатели, но указатель this, переданный в say_hi (), всегда один и тот же, в значительной степени как и ожидалось.

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

Что касается «виртуального», я нашел статью в википедии поучительной , хотя это тоже похоже на проблему с алмазом

0
ответ дан 17 December 2019 в 04:53
поделиться
Другие вопросы по тегам:

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