设置派生对象的基本对象

set the base object of derived object?

本文关键字:对象 设置 派生      更新时间:2023-10-16

这是一个基本的概念问题。如果我有一个从 Base 继承的 Derived 类,并且我实例化了一个新的 Derived 对象,我是否可以将其 Base 对象设置为我选择的特定 Base 对象,以便所有调用基类方法都重定向到这个特定的 Base 对象?

像这样:

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++可能吗?我们可以从对象中剥离派生的信息,那么我们可以分离和替换继承链中的组件吗?

我为什么要这样做?

考虑一下混合百合:(再次,原谅语法错误(

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 可以像装饰器一样使用,而无需装饰器之间的通用接口。

感谢您的回答。由于我们可以切掉对象的派生部分,因此基本信息和派生信息似乎是分开存在的。有人可以对此发表评论吗?我们是否可以访问一些内部表,例如我经常听到的 vtables(我对此类东西一无所知,所以也许这不适用(,并完成此操作?

@Benoît

你能解释一下为什么只有 1 和 4 有效,而 2 和 3 不起作用吗? 类基础 { 保护: 标准::字符串名称; 公共: 基数(标准::字符串 n( { 名称 = 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 << "nn";
Derived d2 = Derived("original 2");
d2.doSomething();
d2.printX();
Base b2 = Base("new 2");
static_cast<Base>(d2) = b2;
d2.doSomething();
d2.printX();
cout << "nn";
Derived d3("original 3");
d3.doSomething();
d3.printX();
Base b3("new 3");
static_cast<Base>(d3) = b3;
d3.doSomething();
d3.printX();
cout << "nn";
Derived d4("original 4");
d4.doSomething();
d4.printX();
Base b4("new 4");
*static_cast<Base*>(&d4) = *&b4;
d4.doSomething();
d4.printX();
cout << "nn";

这将打印:

原创 1x = 5新 1x = 6

原创 2x = 5原创 2x = 6

原创 3x = 5原创 3x = 6

原创 4x = 5新 4x = 6

为什么这只适用于使用指针时?

我不是在质疑你为什么要这样做,但除非你的继承破坏了 ISA 关系,否则这样做是完全安全的(例如 Derived 是 Base 的一个受限子集,例如正方形不是矩形,因为只能调整矩形的一个维度的大小,但不可能用正方形这样做(。

*static_cast<Base*>(d) = *rB;

(参考资料(

或者你可以写一个小函数(你会发现很多函数都是这样做的(:

template<typename T>
T& assign(T& to, const T& from)
{
  return to = from;
}
assign<Base>(*d, *rB);

无论如何,每次重载/重新定义运算符时都这样做=

Derived& operator=(const Derived& other)
{
  // prettier than the cast notation
  Base::operator=(other);
  // do something specific to Derived;
  this->name += " (assigned)";
  return *this;
}

No.如果需要这样做,则应使用组合,而不是继承。

(我正在回答更一般的问题 - 在您的特定情况下,您只想更改字符串,您可以从派生类中更改name(

继承 = IS A关系。

组成=HAS A关系。

您正在描述一个类,该类具有 A 类型的对象,您可以在其中设置其实例。
所以你需要使用组合 - 创建一个包含类型 A 成员的类

继承是类型的属性,而不是对象的属性。类型"派生"继承自类型">基础"。你可以制作类型为"Derived"(Derived x;(的对象,你也可以制作类型为"Base"的对象(Base y,除非这是被禁止的(,但这些对象中的每一个都是完整的、成熟的对象,没有"可替换的部分"。

类型

继承的要点是,您可以将类型为"Derived"的对象视为"Base"类型的对象(只要通过引用或指针引用它(,也就是说,如果您有函数void foo(Base & b);,则可以调用foo(x)。但是foo只能访问从"基地"继承的x部分!

你可以将继承和组合结合起来:

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
}

不,你不能那样做。

您(至少(有几个选择:

  • 创建一个新的Derived对象,由要组合的两个对象的参数混合进行参数化。
  • Base 类中创建一些 setter 方法,这些方法允许您在其生存期后期更改其参数/状态。

No.

你为什么要这样做? 如果两个派生应该具有完全相同的 Base,以便通过一个派生的修改显示在另一个派生中,则需要在每个派生中都有某种指向 Base 的指针,从而形成这种组合,而不是继承。

如果要更改派生 Base 中的数据,请编写一个函数来执行此操作,有点像 Base 的复制赋值运算符。

或者,您可以为派生创建一个新的构造函数,该构造函数将采用 Base 和 Derived,并从 Base 获取 Base 信息,并从 Derived 获取 Derived 信息。