Если вы посмотрите на код ниже, вы увидите, что существуют классы A
, B
и C
, каждый из которых наследуется от последнего. Наследование - virtual
, что означает, что C
должен вызвать конструктор A
, хотя он не наследует напрямую от A
.Виртуальное наследование, конструкторы по умолчанию и дополнительное копирование
Это все хорошо и хорошо, но есть два способа справиться с этим, и они оба кажутся мне взломанными, поэтому я хотел бы знать, действительно ли это так, как вы должны это делать.
Первый способ (код ниже, как это), как представляется, называют A::A(a)
дважды, один раз в B
«конструктор s, а затем снова в C
» конструктор s. Конечно, он не создается дважды, но побочным эффектом этого является то, что параметр a
получает дополнительное время, просто чтобы он мог быть передан в B::B(a, b)
, хотя эта функция не закончила использование значения. Ему нужно только значение для построения A::A(a)
, но здесь этот конструктор вызывается от C
, поэтому копия a
, переданная в B::B(a, b)
, просто выброшена.
Чтобы избежать этой пустой копии, альтернативой является создание дополнительных конструкторов в базовых классах (раскомментируйте приведенный ниже код для этой опции.) Это позволяет избежать потраченной копии, однако недостатком является то, что вы, как программист, создаете полностью новый конструктор для ваших классов, который никогда не вызывается кодом во время выполнения. Вы должны объявить функцию, реализовать ее (даже если она пуста) и на самом деле написать код, который ее вызывает, даже если этот код никогда не будет запущен! Такие вещи обычно сигнализируют о некорректном дизайне, поэтому мне интересно, есть ли другой способ, которым вы должны достичь этого, это менее изворотливое.
#include <iostream>
struct Data {
Data() {
std::cout << "Creating data\n";
}
Data(const Data& d) {
std::cout << "Copying data\n";
}
};
struct A {
A(Data a)
{
std::cout << "A(a)\n";
}
/*
A()
{
std::cout << "A()\n";
}
*/
};
struct B: virtual public A {
B(Data a, Data b)
: A(a)
{
std::cout << "B(a, b)\n";
}
/*
B(Data b)
{
std::cout << "B(b)\n";
}
*/
};
struct C: virtual public B {
C(Data a, Data b, Data c)
: A(a),
B(a, b)
/*
B(b)
*/
{
std::cout << "C(a, b, c)\n";
}
};
int main(void)
{
Data a, b, c;
std::cout << "-- B --\n";
B bb(a, b);
std::cout << "-- C --\n";
C cc(a, b, c);
return 0;
}
EDIT: Просто чтобы прояснить, несколько человек сделал комментарий, что я должен просто пройти по ссылке. Игнорируя тот факт, что это не всегда то, что вы хотите (думайте, умные указатели), оно не устраняет тот факт, что вы передаете значение функции, которая никогда не используется, что кажется изворотливым. Таким образом, выбор - либо передать значение, которое выбрасывается тихо (и надеется, что вы его никогда не укусите), либо реализовать функцию, которую вы никогда не позвоните (и надейтесь, что никогда не вернется, чтобы укусить.)
Итак, мой оригинал вопрос все еще стоит - действительно ли это единственные два способа сделать это? Вы должны выбирать между избыточной переменной или избыточной функцией?
Просто передайте аргументы по ссылке. –
привет Malvineous, мне интересно, почему вы не принимаете ответ Матеуша? – niceman
@niceman Не все патрулируют сайт во все времена.У ОП есть 3k rep и 62 вопроса, большинство из них с принятым ответом. Вы можете подумать, что они знают, как и когда принимать. – Angew