[英]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));
}
注意:我必須將
foo1
和foo2
聲明為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_ptr
和std::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.