问题描述
我的问题在下面的例子中得到了解释。如果在子对象填充其内部数据之前实例化了父对象,那么父对象会丢失其子对象的内部数据是很奇怪的。
代码有一个 TicketService
,它内部保存了一个 TicketStore
实例。 store
填充了 20 个 concerts
。如果代码有以下顺序,一切都按预期进行,服务将可以在商店中看到20场音乐会。
TicketStore store;
Add20Concerts(store);
TicketService service(store); // <- instantiating service after populating the store
// # of concerts in service=20
cout<<"# of concerts in service="<<service.GetNumberOfConcerts()<<endl;
但是,如果在填充商店之前实例化 service
,它会在商店中看到零个音乐会。
TicketStore store;
TicketService service(store); // <- instantiating service before populating the store
Add20Concerts(store);
// # of concerts in service=20
cout<<"# of concerts in service="<<service.GetNumberOfConcerts()<<endl;
为什么 service
的 store
是空的?如果通过传递对 service
的引用来实例化 store
,并且由于 store 将使用默认的复制构造函数,那么底层的 concert_map_
应该已经举办了 20 场音乐会。
更详细的代码框架如下,但上面几乎是我的困惑所在。
*******************************************************************************/
// ticket_service.h
class TicketService {
public:
explicit TicketService(const TicketStore& store) {
store_ = store; // should use the default copy constructor,which is a shallow copy of the original store
}
int32_t GetNumberOfConcerts() {
return store_.GetStoreSize();
}
private:
TicketStore store_;
};
*******************************************************************************/
// ticket_store.h
class TicketStore {
public:
TicketStore() {}
void AddConcert(const Concert& concert) {
concert_map_[concert.id()] = concert;
}
int32 GetStoreSize() const {
return concert_map_.size();
}
private:
absl::flat_hash_map<int64,Concert> concert_map_;
};
*******************************************************************************/
// main.cc
int main(int argc,char* argv[]) {
TicketStore store;
TicketService service(store);
Add20Concerts(store);
// if service is instantiated here,# of concerts in service=20.
// TicketService service(store);
cout<<"store size="<<store.GetStoreSize()<<endl; // store size=20
// # of concerts in service=0 ??
cout<<"# of concerts in service="<<service.GetNumberOfConcerts()<<endl;
}
void Add20Concerts(TicketStore &store) {
for(int i=0; i<10; i++) {
Concert concert;
concert.set_id(i);
store.AddConcert(concert);
}
}
解决方法
TicketService
构造函数正在制作传入它的 TicketStore
对象的副本。在您的第二个示例中,创建 service
对象时该对象为空。创建 store
对象后对 main()
中的 service
对象所做的任何更改都不会反映在该副本中。
对于您正在尝试的内容,TicketService
对象需要持有对 TicketStore
对象的引用,例如:
class TicketService {
public:
explicit TicketService(const TicketStore& store) : store_(store) {}
...
private:
TicketStore& store_; // <-- reference!
};
UPDATE:为了避免任何可能的悬空引用,更好的选择是使用 std::shared_ptr
代替,例如:
class TicketService {
public:
explicit TicketService(std::shared_ptr<TicketStore> store) : store_(store) {}
int32_t GetNumberOfConcerts() {
return (store_) ? store_->GetStoreSize() : 0;
}
private:
std::shared_ptr<TicketStore> store_;
};
void Add20Concerts(TicketStore &store) {
for(int i=0; i<10; i++) {
Concert concert;
concert.set_id(i);
store.AddConcert(concert);
}
}
int main() {
auto store = std::make_shared<TicketStore>();
TicketService service(store);
Add20Concerts(*store);
cout<<"store size="<<store->GetStoreSize()<<endl;
cout<<"# of concerts in service="<<service->GetNumberOfConcerts()<<endl;
}
这样,在 TicketStore
和所有 TicketService
使用完毕之前,shared_ptr
对象保证不会被释放。