开发者

Differing access on a member of a class according to the context

开发者 https://www.devze.com 2023-04-10 20:03 出处:网络
Let\'s say I have a class MainApp using methods of a dynamic library via an interface FrontEnd FrontEnd uses internally instances of a class Data (contained in a class BackEnd)

Let's say I have a class MainApp using methods of a dynamic library via an interface FrontEnd

FrontEnd uses internally instances of a class Data (contained in a class BackEnd)

This class Data only contains a member and its accessor, no public method to manipulate the member

Ok now MainApp has no knowledge of Data and can't access it but it needs indirectly to manipulate it

My goal is to create some kind of a reference keeping a pointer to Data with 2 behaviours

  1. forbidding access to Data when in MainApp
  2. Allowing access to Data when in FrontEnd

I listed some solutions like PIMPL idiom, Bridge or Adaptor pattern but the problem is I don't want to use the Reference like an interface but just like an holder

How can I handle it?

Here some code to illustrate:

Data.hpp

struct Data {
    Data(int i):member(i){}
    int getMember();
private :
    int member;
};

BackEnd.hpp

#include "Data.hpp"

struct BackEnd {
    BackEnd(){}
    Data* createData(int i) {
      Data* d = new Data(i);
      mDatas.push_back(d);
      return d;
    }

    void doSomething(Data* d, int param) {
      int r = param+d->getMember();
      /*do other things with d*/
    }
private:
    vector<Data*> mDatas;
};

Reference.hpp

#include //???

struct Reference {
    Reference(Data* d):mData(d){}
private:
    //no access to data->member
    Data* mData;
};

FrontEnd.hpp

#include "Data.hpp"
#include "Reference.hpp"
#include "BackEnd.hpp"

struct FrontEnd {
    Reference* createData(int i) {
      Data* d = mBackEnd->createData(i);
      //conversion Data to Reference
      Reference ref = new Reference(d);
      return ref;
    }

    void doSomething(Reference* ref) {
      //In the Front-End, I want an access to Ref->mData
      Data* d = ref->getData();//Allows access t开发者_JAVA百科o Ref->mData
      int result = mBackEnd->doSomething(d);
    }
private:
    BackEnd* mBackEnd;
};

MainApp.hpp

//I don't want to reference Data.hpp
#include "Reference.hpp"
#include "FrontEnd.hpp"

struct MainApp {
    Reference* createRef(){ mRef = mFrontEnd->createData(8);}
    void doSomething(){ mFrontEnd->doSomething(mRef); }
private:
    FrontEnd* mFrontEnd;
    Reference* mRef;
    //I want to keep a reference to Data without using Data directly
    //Forbids access to mRef->mData
};


Consider using friendship - i.e. make FrontEnd a friend of Reference, this way you can have a protected member in Reference which will allow you to get the Data*. In Reference.h, include FrontEnd.h (to allow friendship), in FrontEnd.h, use Reference as a forward declaration (i.e. don't include the header). Only include the header in the implementation file for FrontEnd. You'll have to move all the definitions (in FrontEnd.h to the implementation file as well.

Now MyApp can happily hold a Reference but Data* can only be got at by FrontEnd.


If you want to allow external classes access to internals of another you can set it as a friend.

struct Foo;

struct Bar {
private:
  friend struct Foo;
  int count;
};

struct Foo {
  void doBarStuff(Bar &bar) {
    bar.count = 2;
  }
};

This is a trade off obviously. It is alright to think about the two structs differently, but if they access each others internals they don't have much separation of concern. Use sparingly.

When you do this it is good practice to access the internals via method calls as opposed to manipulating them directly. It may seem like you are adding complexity and you are to some extent. It makes your access intentions clear, though.

struct Foo;

struct Bar {
private:
  friend struct Foo;
  void setCount(int c) {
    if (count < 0)
      throw std::logic_error("out of range");
    count = c;
  }
  int count;
};

struct Foo {
  void doBarStuff(Bar &bar) {
    bar.setCount(2);
  }
};

You wouldn't want that count check littered throughout Foo, for example.

0

精彩评论

暂无评论...
验证码 换一张
取 消

关注公众号