簡體   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