Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
97 views
in Technique[技术] by (71.8m points)

c++ - Preserve access privileges when using '->' operator

I have two classes,

template<class Type>
class SafePtr {
public:
    SafePtr() {}
    ~SafePtr() {}

    void Lock(Type* data, void* key)
    {
        if (!pKey)
        {
            pKey = key;
            pData = data;
        }
    }

    Type* Unlock(void* key) const
    {
        if (key == pKey)
            return pData;
    }

    Type* operator->() 
    {
        return pData;
    }

private:
    Type* pData = nullptr;
    void* pKey = nullptr;
};

template<class Type>
class SafePtrArray {
public:
    SafePtrArray() {}
    ~SafePtrArray() {}

    template<class... Args>
    SafePtr<Type> CreatePtr(Args&&... args)
    {
        Type* data = new Type(args...);
        ptrs.insert(ptrs.end(), data);

        SafePtr<Type> ptr;
        ptr.Lock(data, this);
        return ptr;
    }

    Type* UnlockPtr(const SafePtr<int>& ptr)
    {
        return ptr.Unlock(this);
    }

    void Destroy(const SafePtr<int>& ptr)
    {
        Type* pointer = ptr.Unlock(this);

        for (auto itr = ptrs.begin(); itr != ptrs.end(); itr++)
        {
            if ((*itr) == pointer)
            {
                delete pointer;
                ptrs.erase(itr);
            }
        }
    }

private:
    std::vector<Type*> ptrs;
};

Explanation:
The goal is to protect a pointer so that the user can access its members but not get to manipulate its actual pointer (mainly delete it prematurely). And also I need to store all the pointers in an array so that when the parent object destroys, I can automatically destroy all the allocated pointers.
For this I use two classes, SafePtr and SafePtrArray. SafePtrArray creates and stores the pointers and wraps them in the SafePtr and returns it to the user. SafePtr is just a wrapper and should not let the user get access to the underlying pointer but will allow them to access its members.

It works fine at first but soon I found this error,

int main()
{
    SafePtrArray<int> ptr;
    auto pInt = ptr.CreatePtr();
    int* i = pInt.operator->();     // Users can get access to the underlying pointer using this.

    ptr.Destroy(pInt);
}

So my question is,
Is there a way to prevent users from getting access to the underlying type and prevent them from manipulating the pointer while having the privilege to access its members?

question from:https://stackoverflow.com/questions/65850942/preserve-access-privileges-when-using-operator

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

I still think you try to solve a problem that has more to do with possible flaws in the design of the API/of the code, the documentation, or with the lack of C++ knowledge of the one using it, with a "solution" that has more cons than pros.

If a C++ programmer does not know what ownership is or does not respect it and blindly deletes objects or frees the memory of pointers, then there will be much bigger concerns. You likely will move the problem just to a different part of the code.

Having that said, the closest you can do to not expose the pointer right now is something like this:

(The code is just a proof of concept, so things like call might need to be improved)

#include <iostream>
#include <string>

struct Test {
  void foo(int x, int y, std::string str) {
    std::cout << x << " " << y << " " << str << std::endl;
  }

  double test = 0.5;
};

template <typename T>
struct Ptr {

  template <auto M, typename... Args>
  auto call(Args... args) {
    return (obj.*M)(std::forward<Args>(args)...);
  }

  template <auto M>
  auto get() { 
    return (obj.*M);
  }

protected:
  T obj;
};

int main() {
  Ptr<Test> p;

  p.call<&Test::foo>(1, 2, "hello");
  std::cout << p.get<&Test::test>() << std::endl;

  return 0;
}

But I still don't think that this is a good approach.

And a user can still mess around with the code and do something bad like:

int main() {
  Ptr<Test> p;
  
  delete &p;

  return 0;
}

Or this, which for sure is undefined behavior, but that does not really matter as deleting a not owned object will also result in undefined behavior at some point:

template<typename T>
struct Ptr {
protected:
    T *obj;
}

template<typename T>
struct Ptr2 {
public:
    T *obj;
};

int main()
{
    Ptr<Test> p;
    Ptr2<Test> *p2 = reinterpret_cast<Ptr2<Test>*>(&p);
    
    std::cout << p2->obj << std::endl;
}

So there is no protection again such things.

Besides the shown code, there is a proposal for reflection that is feature complete now, which would allow getting information about the members of a type, but this was not added to c++20, and one for metaclasses which is also not in the standard yet.

With these two proposals, you might be able to implement something better useable. But my concerns about the benefits of this remain.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...