这是一个基本概念问题。如果我有一个继承自Base的Derived类,并且我实例化了一个新的Derived对象,我可以将它的Base对象设置为我选择的特定Base对象,以便所有调用基类方法都被重定向到这个特定的基础对象吗? / p>
类似的东西:
class Base
{
protected:
string name;
public:
Base(string n) { name = n}
void doSomething(){cout << name << "\n";}
};
class Derived : public Base
{
public:
Derived(string n) : Base(n) {}
int main()
{
Derived* d = new Derived("original base"); //create a derived
d->doSomething(); // prints "original base"
Base* rB = new Base("replacement base"); // create a new base object
((Base*) d) = rB; // replace the base object of d with a new one (pretend code)
d->doSomething(); // prints "replacement base"
return 0;
}
我确信我在这个简单的代码中犯了各种各样的错误,因为我的技能水平很低,但只是为了这个想法。
这在C ++中是否可行?我们可以从对象中分割派生信息,那么我们可以分离和替换继承链中的组件吗?
我为什么要这样做?
考虑mixin lilies :(再次,原谅语法错误)
template <class T> class MyMixin : public T
{
public:
MyMixin(T desiredBaseObject)
{
// do something to set the desired base
// object of this class to desiredBaseObject.
}
};
RandomClass1 dog(int i = 0);
RandomClass2 cat(double i = 0.0);
MyMixin< RandomClass1 > mixin1(dog);
MyMixin< RandomClass2 > mixin2(cat);
在这种情况下,如果我们可以将mixin的基础对象设置为任何所需的对象,我们可以在mixin中使用带有任何参数列表的构造函数,而mixin不需要知道任何关于它的内容。另外,mixin可以像装饰器一样使用,而不需要装饰器之间的通用接口。
感谢您的回答。由于我们可以切除对象的派生部分,因此基本和派生信息似乎分开存在。有人会对此发表评论吗?我们可以访问一些内部表,就像我听到的那些vtable一样(我对这类东西一无所知,所以也许这不适用),并完成了这个?
@伯努瓦
你能解释为什么只有1和4工作,但2和3不工作吗? 班级基地 { 保护: std :: string name; 上市: Base(std :: string n) { name = n; }
virtual void doSomething()
{
cout << name << "\n";
}
};
class Derived : public Base
{
public:
int x;
Derived(std::string n) : Base(n)
{
x = 5;
}
void printX()
{
cout << "x = " << x << "\n";
x++;
}
};
Derived* d1 = new Derived("original 1");
d1->doSomething();
d1->printX();
Base* rb1 = new Base("new 1");
*static_cast<Base*>(d1) = *rb1;
d1->doSomething();
d1->printX();
cout << "\n\n";
Derived d2 = Derived("original 2");
d2.doSomething();
d2.printX();
Base b2 = Base("new 2");
static_cast<Base>(d2) = b2;
d2.doSomething();
d2.printX();
cout << "\n\n";
Derived d3("original 3");
d3.doSomething();
d3.printX();
Base b3("new 3");
static_cast<Base>(d3) = b3;
d3.doSomething();
d3.printX();
cout << "\n\n";
Derived d4("original 4");
d4.doSomething();
d4.printX();
Base b4("new 4");
*static_cast<Base*>(&d4) = *&b4;
d4.doSomething();
d4.printX();
cout << "\n\n";
这将打印:
原创1 x = 5 新的1 x = 6
原创2 x = 5 原2 x = 6
原创3 x = 5 原创3 x = 6
原创4 x = 5 新4 x = 6
为什么这只适用于使用指针?
答案 0 :(得分:3)
没有。如果你需要这样做,你应该使用组合,而不是继承。
(我正在回答更一般的问题 - 在您特定的情况下,您只想更改字符串,您可以在派生类中更改name
)
答案 1 :(得分:3)
我不是在质疑你为什么要这样做,但除非你的继承打破了ISA关系,否则它是绝对安全的(例如Derived是Base的一个受限子集,例如Square不是Rectangle,因为它可以只调整一个Rectangle的一个维度,但不能用Square调整。
*static_cast<Base*>(d) = *rB;
(也适用于参考文献)
或者你可以写一个小函数(你会发现很多函数都这样做):
template<typename T>
T& assign(T& to, const T& from)
{
return to = from;
}
assign<Base>(*d, *rB);
无论如何,每次重载/重新定义operator =
时都会这样做Derived& operator=(const Derived& other)
{
// prettier than the cast notation
Base::operator=(other);
// do something specific to Derived;
this->name += " (assigned)";
return *this;
}
答案 2 :(得分:1)
继承= IS A
关系。
组成= HAS A
关系。
您正在描述一个类型为 A 的类,您可以在其中设置其实例。
因此,您需要使用合成 - 创建一个包含 A
答案 3 :(得分:1)
继承是类型的属性,而不是对象的属性。 类型“Derived”继承自类型“Base”。您可以创建“Derived”类型的对象(Derived x;
),也可以创建“Base”类型的对象(Base y
,除非被禁止),但每个这些物体是完整的,完全成熟的物体,没有“可更换部件”。
类型继承的一点是,您可以将类型为“Derived”的对象视为它是“Base”类型的对象(只要您通过引用或指针引用它) ),也就是说,如果你有一个函数void foo(Base & b);
,那么你可以调用foo(x)
。但foo
只能访问从“Base”继承的x
部分!
答案 4 :(得分:1)
你可以结合继承&amp;组合物:
class A {
string name;
public: A(const char* s) : name(string(s)) {}
virtual void m() { cout << name << endl; }
};
class B : A {
public:
B(const char* s) : A(s), a(0) {}
void m() { if (a) a->m(); else A::m(); }
A* a;
};
int main() {
B b("b");
b.m(); // prints b
b.a = new A("a");
b.m(); // prints a
}
答案 5 :(得分:0)
不,你做不到。
你(至少)有两个选择:
Derived
对象,由您希望合并的两个对象的参数混合参数化。Base
类中创建一些setter方法,允许您在其生命周期的后期更改其参数/状态。答案 6 :(得分:0)
没有
你为什么要这样做?如果两个Deriveds应该具有完全相同的Base,那么通过一个Derived的修改显示在另一个Derived中,则需要在每个派生中使用某种指向Base的指针,从而构成此组合,而不是继承。
如果要更改Derived's Base中的数据,请编写一个函数来执行此操作,有点像Base的复制赋值运算符。
或者,您可以为Derived创建一个新构造函数,该构造函数将采用Base和Derived,并从Derived中获取Base和Derived信息中的Base信息。