1

这是一个基本的概念问题。如果我有一个继承自 Base 的 Derived 类,并且我实例化了一个新的 Derived 对象,我是否可以将它的 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++ 中可能吗?我们可以将派生信息从一个对象中分割出来,那么我们可以分离和替换继承链中的组件吗?

我为什么要这样做?

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

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

@Benoît

您能解释一下为什么只有 1 和 4 有效,而 2 和 3 无效吗?类 Base { 受保护:std::string 名称;公共:基础(标准::字符串 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 << "\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

为什么这仅在使用指针时才有效?

4

7 回答 7

3

不,如果你需要这样做,你应该使用组合,而不是继承。

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

于 2011-06-21T19:53:57.477 回答
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;
}
于 2011-06-21T22:03:19.077 回答
1

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

类型继承的要点是,您可以将“Derived”类型的对象视为“Base”类型的对象(只要您通过引用或指针引用它),也就是说,如果您有一个函数void foo(Base & b);, 那么你可以打电话给foo(x). 但是foo只能访问x从“Base”继承的那些部分!

于 2011-06-21T20:11:04.627 回答
1

您可以结合继承和组合:

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
}
于 2011-06-21T20:29:46.967 回答
1

继承=IS A关系。

组成=HAS A关系。

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

于 2011-06-21T20:02:02.043 回答
0

不,你不能那样做。

你有(至少)几个选择:

  • 创建一个新Derived对象,由您希望组合的两个对象的参数混合参数化。
  • 在类中创建一些 setter 方法Base,允许您在其生命周期的后期更改其参数/状态。
于 2011-06-21T19:56:28.197 回答
0

不。

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

如果你想改变 Derived 的 Base 中的数据,写一个函数来做这件事,有点像 Base 的复制赋值运算符。

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

于 2011-06-21T20:00:47.013 回答