【问题标题】:How can I have an object of a derived class "reference" members of an instance of the base class?如何让派生类的对象“引用”基类实例的成员?
【发布时间】:2013-08-17 11:13:07
【问题描述】:

假设我有一个类Foo,它的实现涉及一个大对象。我想扩展Foo的功能,所以我决定写一个继承Foo的类Bar

class Foo {
protected:
    int bigobject;
public:
    Foo() : bigobject(1) { }
    int get() { return bigobject; }
    void set(int x) { bigobject = x; }
};


class Bar : public Foo {
public:
    Bar(Foo& f) { /* ??? */ }
    void multiply(int factor) { bigobject *= factor; }
};

如您所见,我将构造函数留空。这是因为我想做的是让任何Bar 对象的基本成员引用现有Foo 实例的成员。换句话说,我想要代码:

Foo f;
f.set(5);
std::cout << "f is " << f.get() << std::endl;

Bar b(f);
std::cout << "b is " << b.get() << std::endl;
b.multiply(2);
std::cout << "b is " << b.get() << std::endl;
std::cout << "f is " << f.get() << std::endl;

结果:

f is 5
b is 5
b is 10
f is 10

这样fb 的基本部分共享相同的内存空间。这是为了避免在我想实例化Bar 的任何时候复制bigobject。用非正式的术语来说,我希望 Bar 对象 b 成为 Foo 对象 f 的“视图”。调用Foo 的任何成员方法都会改变f,但我也可以定义更多方法作为Bar 的一部分,这也会改变f

现在,我可以这样声明一个全局“存储”变量:

int bigobject_storage = 1;

class Foo {
protected:
    int& bigobject;
public:
    Foo() : bigobject(bigobject_storage) { }
    int get() { return bigobject; }
    void set(int x) { bigobject = x; }
};

class Bar : public Foo {
public:
    void multiply(int factor) { bigobject *= factor; }
};

为了获得我想要的功能,但这似乎很骇人听闻并且泄露了实现。那么,有没有一种惯用的 C++ 方法来实现这一点?

【问题讨论】:

    标签: c++ oop inheritance


    【解决方案1】:

    David Wheeler似乎负责报价

    计算机科学中的所有问题都可以通过另一个间接层次来解决。

    这适用于您的情况:让您的基类 Foo 存储一个指向相关大对象的 [智能] 指针,并在相关实例之间共享相应的指针。默认构造Foo 对象时,分配并指向一个大对象。复制Foo 对象时,新的Foo 对象设置为引用与原始对象相同的大对象。但是请注意,这意味着 Foo 基的行为更像是对彼此的引用,而不是值。

    实际上,你可能会使用这样的东西:

    class Foo {
    protected: // I don't really believe in protected data members! Don't use them!
        std::shared_ptr<int> bigobject;
    public:
        Foo(): bigobject(new int()) {}
        Foo(Foo const& other): bigobject(other.bigobject()) {}
        // ...
     };
    
     Bar::Bar(Foo& other): Foo(other) { /*...*/ }
    

    【讨论】:

    • +1 如果您想选择性地将分配排除在Foo 之外(OP 的“hack”的半奇数版​​本),采用shared_ptr&lt;int&gt; 的替代构造函数也足够了外部供应它。很好的答案。
    • 太好了,谢谢!这是我本周提出的第二个问题,其结果取决于初始化列表。我想我会阅读更多关于它们的内容。
    【解决方案2】:

    您只需使用 OOP 概念即可实现您想要的。

    您只需要一个指向对象的指针,多态性就可以完成这项工作。

    您无需对代码进行任何更改,也不必给 Bar 一个 Bar 的副本,您所需要做的就是调用基类的构造函数(尽管这是自动完成的,在默认构造函数),在您的派生类中。

    类似这样的:

    class Foo {
    protected:
        int bigobject;
    public:
        Foo() : bigobject(1) { }
        int get() { return bigobject; }
        void set(int x) { bigobject = x; }
    };
    
    
    class Bar : public Foo {
    public:
        Bar() : Foo() { }
        void multiply(int factor) { bigobject *= factor; }
    };
    

    诀窍在于决定如何“查看”对象。 因为 Foo 是 Bar 的基类,所以 Bar 拥有 Foo 所拥有的一切。换句话说,Bar 是 Foo 加上你在 Bar 中定义的。因此,指向 Bar 的 Foo 指针将表现为 Foo 对象,即使它指向 Bar。

    代码是这样的:

    Bar b();
    /* 
    /* f and b point to the same object.
    /* But, to the compiler, f does'nt know anything of b and its methods,
    /* because f is a pointer to Foo.
     */
    Foo *f = &b;
    f->set(5)
    std::cout << "f is " << f->get() << std::endl;
    
    std::cout << "b is " << b.get() << std::endl;
    b.multiply(2);
    std::cout << "b is " << b.get() << std::endl;
    std::cout << "f is " << f->get() << std::endl;
    

    要得到这个结果:

    f is 5
    b is 5
    b is 10
    f is 10
    

    即使您正在节省一个对象的内存,因为您使用的是同一个对象,但被视为不同的类。这就是多态性。

    【讨论】:

      猜你喜欢
      • 2012-09-17
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2023-03-03
      • 2021-12-05
      • 1970-01-01
      • 2019-04-21
      相关资源
      最近更新 更多