在 C++ 中从另一个初始化 object

Initialize object from another one in C++

在我的项目中,我有资源 objects,可以从不同的格式加载和重新加载。加载算法在不同的ResourceLoader子classes.

中实现
class Resource
{
private:

    // Not for client access    
    Type1 InternalData1;
    Type2 InternalData2;
    TypeN InternalDataN;

    ResourceLoader Loader;

public:

    // Any client interface
    void UseResource();
};

class ResourceLoader
{
public:

    void Load(Resource& R) = 0;
};

class ResourceLoaderFormat1: public ResourceLoader
{
public:

    void Load(Resource& R) { ...loads Resource from Format1... }
};    

class ResourceLoaderFormat2: public ResourceLoader
{
public:

    void Load(Resource& R) { ...loads Resource from Format2... }
};

加载程序读取给定格式的输入并初始化其目标资源object R.加载程序存储在资源中,因此如果资源无效,它会使用存储的加载程序重新加载自身。

问题是 Loader 应该如何初始化资源?

  1. 使所有 InternalData 字段成为 public。它授予客户端访问它们的权限,这并不好。
  2. 使 Loader 成为 Resource 的朋友。特定格式的每个新加载器都将添加到资源 header,从而破坏可扩展性,并要求任何编写扩展的程序员接触基本代码。
  3. 为每个 InternalData 提供 setter。离制作所有这些不远了 public,因为任何客户端都可以更改数据,所以它不能更改。
  4. 提供Resource::Setup(InternalData1、InternalData2、InternalDataN),或将它们全部包装在某种结构中并传递该结构。与3.相同,只是一次设置所有字段。

问题是,资源 class 字段必须可以从可扩展的 class 集合中进行写入,并且必须无法从客户端代码进行写入。有什么好的OOP解决方案吗?谢谢

假设我们选择

Make Loader a friend of Resource.

有缺点

Each new loader for a particular format will be added to a resource header, killing extensibility, and requiring any programmer who writes an extension to touch base code.


但是,由于您将加载程序拆分为基础 class + 派生的 classes,您可以只授予对 Loader 的访问权限,并授予 Loader subclasses 通过 protected 成员访问。

class Resource
{
    Type1 InternalData1;
    ...
    friend class ResourceLoader;
};

class ResourceLoader
{
    ...
protected:
    static void setResourceInternalData1(Resource &r, const Type1 &val1);
    ...
};

所有 ResourceLoader subclasses 现在可以访问这些设置器,因为它们是 protected:

class ResourceLoaderFormat1: public ResourceLoader;
class ResourceLoaderFormat2: public ResourceLoader;

只要您不经常更改 Resource 的数据成员,这就会很好地工作。

另一种解决方案是(如我的第一条评论所述):

class ResourceClient
{
    public:
        virtual void UseResource() = 0;
}
class ResourceLoader
{
    public:
         virtual void SetResource() = 0;
}
class Resource:
    public ResourceClient,
    public ResourceLoader
{
private:

    // Not for client access    
    Type1 InternalData1;
    Type2 InternalData2;
    TypeN InternalDataN;

    ResourceLoader Loader;

public:

    // Any client interface
    virtual void UseResource();
    virtual void SetResource();
};

class ResourceLoader
{
public:

    void Load(ResourceLoader& R) = 0;
};

class ResourceLoaderFormat1: public ResourceLoader
{
public:

    void Load(ResourceLoader& R) { ...loads Resource from Format1... }
};    

class ResourceLoaderFormat2: public ResourceLoader
{
public:

    void Load(ResourceLoader& R) { ...loads Resource from Format2... }
};

需要编写资源的函数集使用 ResourceLoader 指针和受限函数将使用 ResourceClient 指针访问资源。

此解决方案的优点是您不需要声明好友任何 class 或使用写访问权限的函数。只需根据您想做的或想要的功能可以做的,使用正确的界面