C++ 中的只写参考?

发布于 2024-08-30 03:20:50 字数 1114 浏览 13 评论 0原文

有没有办法编写对对象的只写引用?例如,假设有一个互斥类:

template <class T> class mutex {
protected:
   T _data;
public:
   mutex();
   void lock(); //locks the mutex
   void unlock(); //unlocks the mutex
   T& data(); //returns a reference to the data, or throws an exception if lock is unowned
};

是否有一种方法可以保证人们不能这样做:

mutex<type> foo;
type& ref;
foo.lock();
foo.data().do_stuff();
ref = foo.data();
foo.unlock();
//I have a unguarded reference to foo now

另一方面,它是否值得?我知道有些人假设程序员不会故意破坏系统,但是,为什么我们首先要有私有变量,嗯?直接说它是“未定义的行为”就好了,但这似乎有点太不安全了。

编辑:好的,我理解 setter 例程的想法,但是这是如何实现的呢?

mutex<vector<int> > foo;
foo.lock();
for (int i=0; i < 10; i++) {
   foo.data().push_back(i);
}

foo.unlock(); 使用设置例程将需要每次写入都有一个副本:

mutex<vector<int> > foo;
foo.lock();
for (int i=0; i < 10; i++) {
   vector<int> copy = foo.read();
   copy.push_back(i);
   foo.write(copy);
}

虽然您可以在这种特定情况下进行简单的优化,但如果几个不同的线程都在推送元素,甚至可能擦除一些元素,这可能会导致相当多的多余内存复制(即每个关键部分复制一个)。

Is there a way to code a write-only reference to an object? For example, suppose there was a mutex class:

template <class T> class mutex {
protected:
   T _data;
public:
   mutex();
   void lock(); //locks the mutex
   void unlock(); //unlocks the mutex
   T& data(); //returns a reference to the data, or throws an exception if lock is unowned
};

Is there a way to guarantee that one couldn't do this:

mutex<type> foo;
type& ref;
foo.lock();
foo.data().do_stuff();
ref = foo.data();
foo.unlock();
//I have a unguarded reference to foo now

On the other hand, is it even worth it? I know that some people assume that programmers won't deliberately clobber the system, but then, why do we have private variables in the first place, eh? It'd be nice to just say it's "Undefined Behavior", but that just seems a little bit too insecure.

EDIT: OK, i understand the idea of a setter routine, but how would this be accomplished?

mutex<vector<int> > foo;
foo.lock();
for (int i=0; i < 10; i++) {
   foo.data().push_back(i);
}

foo.unlock();
Using a set routine would require a copy for each write:

mutex<vector<int> > foo;
foo.lock();
for (int i=0; i < 10; i++) {
   vector<int> copy = foo.read();
   copy.push_back(i);
   foo.write(copy);
}

though you could trivially optimize in this specific case, if, say, several different threads are all pushing elements, and maybe even erasing a few, this can become quite a bit of excess memory copying (i.e. one per critical section).

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

春花秋月 2024-09-06 03:20:50

是的,您可以创建一个包装类,该包装类在调用解锁时变得无效并返回包装器,而不是返回引用,并且您可以重载其赋值运算符以分配给引用。诀窍在于,您需要保留对包装器内部数据的引用,以便在释放锁之前调用解锁时,使您创建的任何包装器无效。

Yes, you can create a wrapper class that becomes invalidated when unlock is called and return the wrapper, instead of returning the reference, and you can overload its assignment operator to assign to the reference. The trick is that you need to hang onto a reference to the wrapper's internal data, so that when unlock is called, prior to releasing the lock, you invalidate any wrappers that you have created.

任性一次 2024-09-06 03:20:50

区分 getter 和 setter 的常见方法是通过对象的 const 性:

template <class T> class mutex {
public:
   mutex();
   void lock();
   void unlock();
         T& data();       // cannot be invoked for const objects
   const T& data() const; // can be invoked for const objects
protected:
   T _data;
};

现在,如果您想具有只读访问权限,请将互斥体设置为 const:

void read_data(const mutex< std::vector<int> >& data)
{
   // only const member functions can be called here
}

您可以将非常量对象绑定到 const 引用

// ...
mutex< std::vector<int> > data;
data.lock();
read_data(data);
data.unlock();
// ...

lock()unlock() 函数在面对异常时本质上是不安全的:

void f(const mutex< std::vector<int> >& data)
{
  data.lock();
  data.data().push_back(42); // might throw exception
  data.unlock(); // will never be reached in push_back() throws
}

解决这个问题的常用方法是RAII(资源获取就是初始化):

template <class T> class lock;

template <class T> class mutex {
public:
   mutex();
protected:
   T _data;
private:
   friend class lock<T>;
   T& data();
   void lock();
   void unlock();
};

template <class T> class lock {
public:
  template <class T> {
  lock(mutex<T>& m) m_(m) {m_.lock();}
  ~lock()                 {m_.unlock();}

         T& data()        {return m_.data();}
   const T& data() const  {return m_.data()}
private:
  mutex<T>& m_;
};

请注意,我还将访问器函数移到了锁类中,这样就无法访问未锁定的数据。

你可以这样使用它:

void f(const mutex< std::vector<int> >& data)
{
  {
    lock< std::vector<int> > lock_1(data);
    std::cout << lock1.data()[0]; // fine, too
    lock1.data().push_back(42);   // fine
  }
  {
    const lock< std::vector<int> > lock_2(data); // note the const
    std::cout << lock1.data()[0];  // fine, too
    // lock1.data().push_back(42); // compiler error
  }
}

The common way to differentiate between getters and setters is by the const-ness of the object:

template <class T> class mutex {
public:
   mutex();
   void lock();
   void unlock();
         T& data();       // cannot be invoked for const objects
   const T& data() const; // can be invoked for const objects
protected:
   T _data;
};

Now, if you want to have read-only access, make the mutex const:

void read_data(const mutex< std::vector<int> >& data)
{
   // only const member functions can be called here
}

You can bind a non-const object to a const reference:

// ...
mutex< std::vector<int> > data;
data.lock();
read_data(data);
data.unlock();
// ...

Note that the lock() and unlock() functions are inherently unsafe in the face of exceptions:

void f(const mutex< std::vector<int> >& data)
{
  data.lock();
  data.data().push_back(42); // might throw exception
  data.unlock(); // will never be reached in push_back() throws
}

The usual way to solve this is RAII (resource acquisition is initialization):

template <class T> class lock;

template <class T> class mutex {
public:
   mutex();
protected:
   T _data;
private:
   friend class lock<T>;
   T& data();
   void lock();
   void unlock();
};

template <class T> class lock {
public:
  template <class T> {
  lock(mutex<T>& m) m_(m) {m_.lock();}
  ~lock()                 {m_.unlock();}

         T& data()        {return m_.data();}
   const T& data() const  {return m_.data()}
private:
  mutex<T>& m_;
};

Note that I have also moved the accessor functions to the lock class, so that there is no way to access unlocked data.

You can use this like this:

void f(const mutex< std::vector<int> >& data)
{
  {
    lock< std::vector<int> > lock_1(data);
    std::cout << lock1.data()[0]; // fine, too
    lock1.data().push_back(42);   // fine
  }
  {
    const lock< std::vector<int> > lock_2(data); // note the const
    std::cout << lock1.data()[0];  // fine, too
    // lock1.data().push_back(42); // compiler error
  }
}
无悔心 2024-09-06 03:20:50

您可以将数据封装为私有数据并公开写入例程。在该例程中,您可以锁定互斥锁,从而获得与您所要拍摄的内容类似的行为。

You could encapsulate the data as private and expose a write routine. Within that routine you could lock your mutex, giving you similar behavior to what you are shooting for.

怎会甘心 2024-09-06 03:20:50

您可以按如下方式使用成员函数:

void set_data(const T& var);

这就是在 C++ 中应用只写访问的方式。

You can use a member function as the following:

void set_data(const T& var);

This is how write-only access is applied in C++.

似狗非友 2024-09-06 03:20:50

不。没有办法保证在 C++ 等不安全语言中读写内存的任何事情,其中​​所有内存都被视为一个大数组。


[编辑] 不确定为什么所有的反对票都投了;这是正确且相关的。

在安全语言(例如 Java 或 C#)中,您当然可以保证正确实现的不可变类型将保持不可变。在 C++ 中永远无法做出这样的保证。

与其说担心的是恶意用户,不如说担心的是意外的无效指针;我曾参与过 C++ 项目,其中不可变类型由于完全不相关的代码中的无效指针而发生了变异,从而导致难以追踪的错误。这种保证——只有安全语言才能做到——既有用又重要。

No. There is no way to guarantee anything about reading and writing memory in unsafe languages like C++, where all the memory is treated like one big array.


[Edit] Not sure why all the downvotes; this is correct and relevant.

In safe languages, such as Java or C#, you can certainly guarantee that, for instance, properly-implemented immutable types will stay immutable. Such a guarantee can never be made in C++.

The fear is not so much malicious users as it is accidental invalid-pointers; I have worked on C++ projects where immutable types have been mutated due to an invalid pointer in completely unrelated code, causing bugs that are extremely difficult to track down. This guarantee - which only safe languages can make - is both useful and important.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文