繁体   English   中英

将指针用作类成员是一种好习惯吗?

[英]Is it good practice to use pointers as class members?

我是C ++的新手,我正在努力理解构建类的良好实践。

假设我有一堂课Foo

class Foo {
  public:
    double foo;
    Foo(double foo);
    Foo add(Foo f);
}

我想创建一个类Bar ,它由两个Foo对象组成,并在构造时创建第三个。

第一个选项:作为类成员的对象

class Bar {
  public:
    Foo foo1;
    Foo foo2;
    Foo foo3;
    Bar(const Foo &f1, const Foo &f2);  
}

Bar::Bar(const Foo &f1, const Foo &f2):
{
  foo1 = f1;
  foo2 = f2;
  foo3 = f1.add(f2);
}

因为我没有为Foo定义默认构造函数,所以它不起作用。

第二种选择:指针作为班级成员

class Bar {
  public:
    const Foo* foo1;
    const Foo* foo2;
    const Foo* foo3;
    Bar(const Foo &f1, const Foo &f2);  
}

Bar::Bar(const Foo &f1, const Foo &f2):
{
  foo1 = &f1;
  foo2 = &f2;
  foo3 = &(f1.add(f2));
}

注意:我必须将foo1foo2声明为const才能使构造函数正常工作。 它仍然失败,因为对于foo3我正在取一个临时结果的地址,这是非法的。


哪个选项更自然(我如何修复错误)? 我觉得第一个选项可能更好,但是我的Foo对象必须在内存中创建两次,不是吗? (一次调用构造函数,第二次调用构造函数本身)

任何帮助赞赏。

使用指针作为成员是好的,但在你的情况下,你只是在一个小的打嗝工作,实际上不保证指针的使用,并且使用指针可能是危险的,我将很快指出一个问题。

因为我没有为Foo定义默认构造函数,所以它不起作用。

使用Bar的初始化程序可以轻松解决这个问题:

Bar(const Foo &f1, const Foo &f2) : foo1(f1), foo2(f2), foo3(f1.add(f2)) {}

如下所示:

#include <iostream>

class Foo {
  public:
    double m_foo;
    Foo(double foo) : m_foo(foo) {}
    Foo add(Foo f) { f.m_foo += m_foo; return f; } // returns temporary!
};

class Bar {
  public:
    Foo m_foo1;
    Foo m_foo2;
    Foo m_foo3;
    Bar(const Foo &foo1, const Foo &foo2);  
};

Bar::Bar(const Foo &foo1, const Foo &foo2)
    : m_foo1(foo1)
    , m_foo2(foo2)
    , m_foo3(m_foo1.add(m_foo2))
{
}

int main() {
    Foo foo1(20.0);
    Foo foo2(22.0);
    Bar bar(foo1, foo2);

    std::cout << bar.m_foo3.m_foo << "\n";

    return 0;
}

现场演示: http//ideone.com/iaNzJv

在你的指针解决方案中,你引入了一个明显的指针问题:一个指向临时的指针。

foo3 = &(f1.add(f2));

f1.add返回一个临时Foo,你获取地址,然后它就消失了。 这是一个悬垂的指针。

您的指针实现也没有显式地将指针作为其输入,因此f1和f2可能会遇到同样的问题:

Bar(Foo(20), Foo(22));  // two temporary Foos passed by reference
                        // but having their addresses taken. ouch.

如果你正在指点,最好在你班上的api那样做; 你将不得不关心指向的事物的生命周期,并试图让调用者更容易告诉你这样做。

Bar(Foo* f1, Foo* f2);

但是现在如果你要拥有F3,你将负责管理它的内存:

Bar(Foo* f1, Foo* f2)
    : foo1(f1), foo2(f3), foo3(new Foo(*f1.add(*f2)))
{}

~Bar()
{
    delete f3;
}

因此,在您的示例中,使用成员可能会更好。

保存对您绝对不想复制的大对象的指针的使用,以及不能使用移动操作的地方。

---编辑---

在现代C ++(C ++ 11及更高版本)中,通过“智能指针”,特别是std::unique_ptrstd::shared_ptr ,已经在很大程度上解决了传递指针所有权的问题。

尽管它需要学习一些较新的C ++概念,但它通常被认为是使用这些而不是原始指针的最佳实践。

#include <memory>

struct Foo {};
class Bar {
public:
    std::unique_ptr<Foo> m_f1; // we will own this
    std::unique_ptr<Foo> m_f2; // and this

    Bar(std::unique_ptr<Foo> f1) // caller must pass ownership
        : m_f1(std::move(f1))    // assume ownership
        , m_f2(std::make_unique<Foo>()) // create a new object
    {}

    ~Bar()
    {
        // nothing to do here
    }
};

int main() {
    auto f = std::make_unique<Foo>();
    Bar(std::move(f)); // the 'move' emphasizes that
                       // we're giving you ownership
    // 'f' is now invalid.

    return 0;
}

现场演示: http//ideone.com/9BtGkn

优雅的是,当Bar超出范围时, unique_ptr将确保他们拥有的对象被我们销毁 - 我们不必记得delete它们。

在上面的例子中,将m_f2作为成员而不是指针可能要好得多。

如果对象不是太昂贵而无法传递,我建议使用对象作为成员。

如果由于某种原因需要使用指针,则需要制定所有权策略。 Bar对象是否拥有对象? Bar只保存指向对象的指针,但不负责释放它们使用的资源吗?

如果Bar拥有Foo对象,则更喜欢使用其中一个智能指针。 您需要使用new来复制这些对象并保留这些指针。

这是我的看法:

class Bar {
  public:
    std::unique_ptr<Foo> foo1;
    std::unique_ptr<Foo> foo2;
    std::unique_ptr<Foo> foo3;
    Bar(const Foo &f1, const Foo &f2) : foo1(new Foo(f1)), ... {}
};

std::unique_ptr没有复制构造函数。 因此,您必须为Bar提供复制构造函数,并从副本中适当地初始化其成员。

如果Bar不拥有Foo对象,您可以通过使用引用作为成员数据来获取。

class Bar {
  public:
    Foo const& foo1;
    Foo const& foo2;
    Foo const& foo3;
    Bar(const Foo &f1, const Foo &f2) : foo1(f1), ... {}
};

我认为对象与原始变量相同是无稽之谈。

class Foo {
  public:
    double _stocks;
    Business* _business;
    Foo(double stocks, Business* business):_stocks(stocks), _business(business){}
    Foo* add(const Foo& f) {
        _stocks += f._stocks;
        _busines->merge(f._business);
        return this;
    }
    virtual ~Foo() {  delete _business;  }
}
class Bar {
  public:
    Foo* _foo1;
    Foo* _foosub;
//    Foo* _foo3;
    Bar(Foo* f1, Foo* f2); // unable const for f1 at least 
}
Bar::Bar(Foo* f1, Foo* f2):
{
    _foo1 = f1;
    _foosub = f2;
    _foo1.add(*f2);
    // _foo3 is the same as _foo1
}
void main() {
    Foo company1(100.00, BusinessFactory.create("car"));
    Foo company2(2000.00, BusinessFactory.create("food"));
    Bar conglomerate(&company1, &company2);
    // to be continued
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM