请考虑以下代码:
#include <iostream>
#include <string>
struct Thing {
std::string name;
int width, length, height, mass;
Thing (const std::string& n) : name(n) {}
Thing (const std::string& n, int w, int l, int h, int m) :
name(n), width(w), length(l), height(h), mass(m) {}
void print() const {std::cout << name << ", " << width << ", " << length << ", " << height << ", " << mass << '\n';}
virtual void foo() = 0; // Abstract class
};
struct Clone : virtual Thing {
Thing& parent;
Clone (const std::string& name, Thing& p) : Thing(name), parent(p) {}
};
template <typename T>
struct ClonedType : public Clone, public T {
ClonedType (const std::string& name, Thing& t) :
Thing(name), Clone(name, t), T(name) {}
};
// virtual inheritance needed because of ClonedType<Blob> being instantiated:
struct Blob : virtual Thing {
Blob (const std::string& name) : Thing(name, 3, 4, 5, 20) {}
virtual void foo() override {}
};
int main() {
Blob blob("Blob");
ClonedType<Blob> b("New Blob", blob);
blob.print(); // Blob, 3, 4, 5, 20
b.print(); // New Blob, -1, -1, 4237013, 0 // Here's the problem!
}
由于虚拟继承, Blob
的构造函数调用Thing
的构造函数很好,但由于虚拟继承ClonedType<T>
无法调用Thing
的构造函数通过使用T
的构造函数。因此,b
类型Blob
未正确初始化(所有Blob对象将共享相同的值3,4,5,20以及其他值,如字符串和特殊类型我没有在这里展示。那么如何解决这个问题,除了在Blob构造函数体中手动设置这些值(这会破坏Thing(name, 3, 4, 5, 20)
的目的)?顺便说一句,事情是一个抽象的阶级。
更新 我在下面添加了一个适用于上述问题的解决方案,但是在该解决方案中,我为这个问题添加了更多复杂性,使新问题得不到解决。
答案 0 :(得分:0)
看起来您通过调用Thing()
在Clone
内初始化Thing(name)
。初始化一次后,构造函数将不再被调用。
我不认为虚拟继承真的是你在寻找的东西。您可能想要创建一个单独的Thing
对象而不是继承它,但将其作为成员。
答案 1 :(得分:0)
对于virtual
个基础对象,最派生的类是负责调用virtual
base的构造函数的类。从中间类调用virtual
base Thing
只是被忽略了。没有办法解决这个问题。也就是说,问题变成:一些中间类如何安排最派生的类用适当的参数调用Thing
?
假设您的Thing
恰好是具体的,一种可能的方法可能是由某些基类创建临时Thing
,例如在Blob
中并且具有进一步的派生类抓住它将它传递给Thing
构造函数,例如:
template <typename T>
ClonedType<T>::ClonedType(std::string const& name)
: Thing(T::get_initializer(name))
, Clone(name)
, T(name) {
}
当然,这意味着,例如Blob
有一个合适的static
方法get_initializer()
:
auto Blob::get_initializer(std::string const& name) -> Thing {
return Thing(name, 3, 4, 5, 20);
}
虽然我认为这会使初始化工作起作用,但我通常会对设计提出疑问:通常virtual
基数已经表明存在问题。如果它基本上是无状态的并且只有一些抽象函数,那么可能是合理的。当然,我对使用virtual
关键字非常怀疑:它很少有用。
答案 2 :(得分:0)
好的,我找到了一个有效的解决方案,其中添加的信息Thing
是抽象的(从而使解决方案变得更难,上述解决方案无效):
#include <iostream>
#include <string>
struct Thing {
struct PartialData { int width, length, height, mass; }; //**Added
struct PartialDataTag {}; //**Added
std::string name;
int width, length, height, mass;
Thing() = default;
Thing (const std::string& n) : name(n) {}
Thing (const std::string& n, int w, int l, int h, int m) :
name(n), width(w), length(l), height(h), mass(m) {}
Thing (const std::string& n, const PartialData& data) : // ***Added
name(n), width(data.width), length(data.length), height(data.height), mass(data.mass) {}
void print() const {std::cout << name << ", " << width << ", " << length << ", " << height << ", " << mass << '\n';}
protected:
void setUsingPartialData (const PartialData& data) { // ***Added
width = data.width; length = data.length; height = data.height; mass = data.mass;
}
virtual void foo() = 0; // Abstract class
};
struct Clone : virtual Thing {
Thing& parent;
Clone (const std::string& name, Thing& p) : Thing(name), parent(p) {}
};
template <typename T>
struct ClonedType : public Clone, public T {
ClonedType (const std::string& name, Thing& t) :
Thing(name), Clone(name, t), T(PartialDataTag{}) {} // ***Changed
};
struct Blob : virtual Thing {
static const PartialData partialData;
Blob (const std::string& name) : Thing (name, partialData) {}
Blob (PartialDataTag) {setUsingPartialData(partialData);} // ***Added
virtual void foo() override {}
};
const Thing::PartialData Blob::partialData = {3, 4, 5, 20}; // ***Added
int main() {
Blob blob("Blob");
ClonedType<Blob> b("New Blob", blob);
blob.print(); // Blob, 3, 4, 5, 20
b.print(); // New Blob, 3, 4, 5, 20
}
有人能想到更好的解决方案吗?许多新添加的东西使它工作,但至少信息3, 4, 5, 20
在这里只使用一次。
然而,我可以想到这个解决方案的严重缺点。假设从Thing
派生的另一个具体类使用不同类型的部分数据(仅对width
和mass
说{int,int}),那么我上面的解决方案不会起作用对于这个新课,对吧?
添加到问题:
struct Sample : virtual Thing {
Sample (const std::string& name, int length, int height) :
Thing(name, 10, length, height, 50) {}
virtual void foo() override {}
};
如何实例化
ClonedType<Sample>
并正确初始化?所有Sample
个对象的宽度为10,质量为50.哦,我们还假设Thing
有更多std :: string数据成员(Sample
的静态值也是如此)这样我们就不能使用模板(例如Sample:Data&lt; 10,50&gt;)来定义Sample
类。
更新:已解决!
#include <iostream>
#include <string>
struct Thing {
struct PartialData { std::string codeName; int width, length, height, mass; };
struct PartialDataTag {};
std::string name, codeName;
int width, length, height, mass;
Thing() = default;
Thing (const std::string& n) : name(n) {}
Thing (const std::string& n, int l, int h) : name(n), length(l), height(h) {}
Thing (const std::string& n, const std::string& c, int w, int l, int h, int m) :
name(n), codeName(c), width(w), length(l), height(h), mass(m) {}
Thing (const std::string& n, const PartialData& data) :
name(n), codeName(data.codeName), width(data.width), length(data.length), height(data.height), mass(data.mass) {}
void print() const {std::cout << name << ", " << codeName << ", " << width << ", " << length << ", " << height << ", " << mass << '\n';}
protected:
void setUsingPartialData (const PartialData& data) {
codeName = data.codeName; width = data.width; length = data.length; height = data.height; mass = data.mass;
}
virtual void foo() = 0;
};
struct Clone : virtual Thing {
Thing& parent;
template <typename... Args>
Clone (Thing& p, Args&&... args) : Thing (std::forward<Args>(args)...), parent(p) {}
};
template <typename T>
struct ClonedType : public Clone, public T {
template <typename... Args>
ClonedType (Thing& t, Args&&... args) : Thing (std::forward<Args>(args)...), Clone(t, std::forward<Args>(args)...), T(PartialDataTag{}) {}
};
struct Blob : virtual Thing {
static const PartialData partialData;
Blob (const std::string& name) : Thing (name, partialData) {}
Blob (PartialDataTag) {setUsingPartialData(partialData);}
virtual void foo() override {}
};
const Thing::PartialData Blob::partialData = {"Bob", 3, 4, 5, 20}; // !
struct Sample : virtual Thing {
static const int staticWidth = 10, staticMass = 50;
Sample (const std::string& name, int length, int height) : Thing(name, "Sam", staticWidth, length, height, staticMass) {}
Sample (PartialDataTag) {setUsingPartialData(getPartialData());}
virtual void foo() override {}
PartialData getPartialData() const {return {"Sam", staticWidth, length, height, staticMass};} // !
};
int main() {
Blob blob("Blob");
ClonedType<Blob> b(blob, "New Blob");
blob.print(); // Blob, Bob, 3, 4, 5, 20
b.print(); // New Blob, Bob, 3, 4, 5, 20
Sample sample("Sample", 11, 12);
ClonedType<Sample> s(sample, "New Sample", 21, 22);
sample.print(); // Sample, Sam, 10, 11, 12, 50
s.print(); // Sample, Sam, 10, 21, 22, 50
}