首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >一对多多重模式

一对多多重模式
EN

Code Review用户
提问于 2014-09-22 04:31:19
回答 2查看 2K关注 0票数 8

下面的工作C++11代码演示了用于表示对象之间一对多关系的模式的工作代码。

GitHub

(该代码的一个变体也会在本文底部内联复制)。

我认为这些都是可取的特征:

  • 子级仅通过拥有父级创建,以便由父级管理。
  • 用于内存管理的std::shared_ptr和相关实用程序。
  • const-correctness

备注:

  • 指向GitHub的链接使用抽象名称(A,B),而不是这里使用的具体示例(模型、组件)。
  • GitHub版本中的抽象名称提示可能的模板实现。

Model.h

代码语言:javascript
复制
#ifndef Model_h
#define Model_h

#include "Component.h"
#include <vector>
#include <string>


//! For collection of const children (i.e. each element immutable).
//! Overall collection is also not mutable
typedef std::vector<std::shared_ptr<const Component>> const componentCollectionConstT;

//! Overall collection is not mutable, but each element is mutable
typedef std::vector<std::shared_ptr<Component>> const componentCollectionT;

//! The class for a parent object
class Model :
  public std::enable_shared_from_this<Model> // Needed so that a reference to the Model parent object creating the Component child object can be stored by the child component object.
{
    //! Children
    std::vector<std::shared_ptr<Component>> components;

    //! Model name
    std::wstring mName;

public:
    //! Construct passing in value for name.
    //! \param name will be stored as the model name.
    Model(const std::wstring name);

    //! Getter for name
    //! \return the name
    const std::wstring getName() const;

    /** Get read-only collection of children components
     *
     * Each child element is immutable.
     * \return The collection of children, read-only.
     */
    componentCollectionConstT getComponentsReadOnly() const;

    /** Get collection of mutable children
     *
     * Each child element is mutable.
     * Overall membership of collection is still fixed.
     * \return The collection of mutable children.
     */
    componentCollectionT getComponents();

    /**
     * Create a child component object, of which this object is the parent
     * \return a pointer the newly created child component object.
     */
    const std::shared_ptr<const Component> createComponent(int);
};

#endif

Model.cpp

代码语言:javascript
复制
#include "Model.h"
#include <memory>
#include <algorithm>

using namespace std;

Model::Model(wstring name) :
  mName(name)
{}


const wstring Model::getName() const {
    return mName;
}


componentCollectionConstT Model::getComponentsReadOnly() const {
    componentCollectionConstT componentsC(components.begin(), components.end()); // Have to copy to get const version.
    return componentsC;
}


componentsCollectionT Model::getComponentss() {
    return components;
}


const shared_ptr<const Component> Model::createComponent(int count) {
    shared_ptr<Component> componentSP(new Component(count, shared_from_this())); // Have to use "shared_from_this"
    components.push_back(componentSP);
    return componentSP;
}

Component.h

代码语言:javascript
复制
#ifndef Component_h
#define Component_h

#include <memory>

class Model;

//! Components group aspects of the model.
class Component {
    friend class Model;

    //! Constructor kept private so that only Model (which is a friend) can construct. \see Model::createComponent
    Component(const int count, std::weak_ptr<Model> model);

    //! Some arbitrary data member as a place-holder for more interesting content in the real world.
    int mCount;

    //! Reference from child back to parent.
    //! This needs to be a weak_ptr to avoid circular ref counting issue.
    std::weak_ptr<Model> mModel;

public:
    //! Getter for arbitrary data member.
    const int getCount() const;

    //! Setter for arbitrary data member.
    void setCount(const int);

    //! Get a weak reference to parent model object.
    const std::weak_ptr<const Model> getModel() const;
};


#endif

Component.cpp

代码语言:javascript
复制
#include "Component.h"
#include "Model.h"

using namespace std;

Component::Component(const int count, weak_ptr<Model> model):
  mCount(count),
  mModel(model)
{}


const int Component::getCount() const {
    return mCount;
}

void Component::setCount(const int count) {
    mCount = count;
}

const weak_ptr<const Model> Component::getModel() const {
    return mModel;
}

main.cpp

代码语言:javascript
复制
/**
 * Demonstrates one-to-many relationship with parent ownership semantics,
 * and bi-directional navigability.
 * Memory management using std::shared_ptr and related utilities.
 *
 * Components are created through Model, so that they are never orphans.
 * Also, the collection of children of a Model object cannot have members added or removed.
 *
 * So far this has only been tested on XCode 5.1.1 and 6.0.1
 */

#include <iostream>
#include <memory>

#include "Model.h"
#include "Component.h"

using namespace std;


int main(int argc, const char * argv[])
{

    // Demonstrate creating a parent model, and creating child Components through the parent.
    // The parent is mutable.
    // Recommendation: all handle's are shared_ptr. (Or perhaps mandatory, since createComponent calls "shared_from_this"?)
    shared_ptr<Model> a(new Model(L"The first model object"));
    a->createComponent(5);
    a->createComponent(55);
    a->createComponent(5555);
    a->createComponent(-345);
    a->createComponent(2316724);
    a->createComponent(0);
    componentCollectionT bs = a->getComponents();
    //components.push_back(make_shared<Component>(42)); // Compile error <= const (otherwise new children could be "smuggled in"); and Component constructor private.
    for(auto b : bs) {
        b->setCount(b->getCount()*10000); // Demonstrate that each child is mutable.
        auto aName = b->getModel().lock()->getName();
        wcout << b->getCount() << " - " << aName  << endl;
    }

    cout << "**********************" << endl;

    // Repeat the above output (this demonstrates that we really modified the underlying children, not just copies of them.
    for(auto b : a->getComponents()) {
        wcout << b->getCount() << " - " << b->getModel().lock()->getName() << endl;
    }

    cout << "**********************" << endl;

    // If we start out with a const parent (i.e. instance of Model), we can't even add children!
    const shared_ptr<const Model> ac = const_pointer_cast<const Model>(make_shared<Model>(L"The second model object"));
//    ac->createComponent(5); // Compile error <= const.
//    componentCollectionT bsc = ac->getComponents(); // Compile error <= const.
    componentCollectionConstT bsc = ac->getComponentsReadOnly();
//    bsc[1]->setCount(444); // Compile error <= const.
//    bsc.push_back(1); // Compile error <= const.
    for(auto b : bsc) {
        wcout << b->getCount() << " - " << b->getModel().lock()->getName() << endl;
    }

    cout << "**********************" << endl;

    // If we have a const child, then the handle we get to its parent is also const.
    const shared_ptr<Component const> b1(a->createComponent(21112));
    auto ac2 = b1->getModel().lock();
    auto bsc2 = ac2->getComponentsReadOnly();

    for(auto b : bsc2) {
        wcout << b->getCount() << " - " << b->getModel().lock()->getName() << endl;
    }

//    ac2->createComponent(5); // Compile error <= const.
//    componentCollectionT bsc22 = ac2->getComponents(); // Compile error <= const.
//    bsc2[1]->setCount(444); // Compile error <= const.
//    bsc2.push_back(1); // Compile error <= const.


    return 0;
}
EN

回答 2

Code Review用户

发布于 2014-09-25 02:20:37

我不认为这种模式对你有好处。主要原因是我不认为Model应该包含Component的实例。

我必须创造一个具体的例子来解释这一点。我们在StackExchange上,所以让我们说一个用户有很多问题。按照您的模式,我们如何获得一个列表(多个)的问题尚未得到回答?我们必须遍历每一个用户,看看用户的问题,

相反,我们应该从数据库中单独的“表”开始:

代码语言:javascript
复制
std::vector< User > all_users;
std::vector< Questions > all_questions;

问题必须“属于”一个(而且只有一个)用户,因此这种关系可以通过一个简单的引用来实现。这个用户必须跟踪他/她的问题,为此我们可以使用std::reference_wrapper (在<functional>中找到)。

代码语言:javascript
复制
class Question
{
  public:
  Question( User& user ) : user( user ){  user.ask( *this ); }

  protected:
  User& user;
};

class User
{
  public:
  void ask( Question& question )
  {
    questions.push_back( question );
  }

  protected:
  std::vector< std::reference_wrapper< Question > > questions;
};

这种设置要求在“删除”元素时要小心,特别是当用户删除他/她的帐户时:

代码语言:javascript
复制
~Question( void )
{
  user.unAsk( *this );
}

~User( void )
{
  for ( auto& question: questions )
    question.invalidate();
}

至于const正确性,实际上,它不需要更改函数名(或复制):

代码语言:javascript
复制
const std::vector< std::reference_wrapper<Question> >& User::getQuestions( void ) const
{
  return questions;
}

std::vector< std::reference_wrapper<Question> >& User::getQuestions( void )
{
  return questions;
}

在我们代码中的某个地方:

代码语言:javascript
复制
void function( const User& user )
{
  user.getQuestions()[0].get().text = "edit question"; // compile error
}

void anotherFunction( User& user )
{
  user.getQuestions()[0].get().text = "edit question"; // just fine
}
票数 4
EN

Code Review用户

发布于 2015-01-16 23:41:18

在这里,我将评论OP列出的两种特定特性,它们都是他的代码所需要的:

  • 子级仅通过拥有父级创建,以便由父级管理。
  • 用于内存管理的std::shared_ptr和相关实用程序。

如果孩子由他们的父母“管理”,那么破坏父母就应该消灭孩子,IMHO。但是返回(shared_ptr的一个向量)S允许getComponents()的调用者存储这些shared_ptrs,只有当所有的引用都没有时,孩子们才会被销毁。毕竟,shared_ptr真的是关于,呃,共有的.

我的另一个观点是,“使用shared_ptr和相关实用程序”本身不应该是一个目标。这不是说这不是个好主意。这些都是工具,应该使用最好的工具。在这种情况下,我怀疑它们是否有用.假设父母完全拥有自己的孩子,它可以使用原始指针的向量,而不是shared_ptrs的向量。在CPU和内存方面,shared_ptrs比原始指针昂贵得多。原始指针的唯一缺点是父类的析构函数必须显式地销毁它的所有子元素:原始指针的析构函数不起多大作用。;-)

这可能意味着最好的工作工具可能是std::unique_ptr.但我稍后会研究这个问题..。

票数 2
EN
页面原文内容由Code Review提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://codereview.stackexchange.com/questions/63525

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档