当类对象超出范围之前可以删除数据时,如果类允许访问其数据(通过 ptr/it),这是否是糟糕的设计?
经典的例子是迭代器失效:
std::string test("A");
auto it = test.insert(test.begin()+1,'B');
test.erase();
...
std::cout << *it;
您是否认为这种 API 是糟糕的设计,并且对于初学者来说很难学习/使用?
在这种情况下,一个成本高昂、性能/内存方面的解决方案是,当使用clear方法时,将指针/迭代器分配给空字符串(或nullptr,但这不是很有帮助)。
一些精度
我正在考虑这种设计,用于返回可以在内部修改的 const chars* (也许它们存储在可以清除的 std::vector 中)。我不想返回 std::string (二进制兼容性),也不想使用 get(char*,std::size_t) 方法,因为需要获取大小参数(太慢)。另外,我不想围绕 std::string 或我自己的字符串类创建包装器。
Classic example is iterator invalidation :
std::string test("A");
auto it = test.insert(test.begin()+1,'B');
test.erase();
...
std::cout << *it;
Do you think having this kind of API is bad design, and will be difficult to learn/use for beginners ?
A costly, performance/memory wise, solution would be, in that type of case, to assign the pointer/iterator to an empty string (or a nullptr, but that's not very helpful) when a clear method is used.
Some precisions
I'm thinking of this design for returning const chars* that can be modified internally (maybe they're stored in a std::vector that can be cleared). I don't want to return a std::string (binary compatibility) and I don't want a get(char*,std::size_t) method because of the size argument that needs to be fetched (too slow). Also I don't want to create a wrapper around std::string or my own string class.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
我建议阅读Stepanov的设计理念(第9-11页):
Stepanov 还有很多其他有趣的文档,特别是在“课堂笔记”部分。
是的,有一些关于 OOP 的经验法则。不,我不相信它们真的是最好的做事方式。当您使用 STL 时,以 STL 兼容的方式做事很有意义。当您的抽象级别较低时(例如
std::vector
,专门用于使动态分配的数组更容易使用;即,它应该几乎像具有一些附加功能的数组一样可用),那么一些 OOP 经验法则就完全没有意义了。回答最初的问题:即使是初学者最终也需要了解迭代器、对象生命周期以及我所说的对象的使用寿命(即“对象尚未超出范围,但不再有效使用” ,就像一个无效的迭代器”)。我认为没有任何理由试图向用户隐藏这些生活事实,因此我个人不会基于这些理由排除基于迭代器的 API。真正的问题是您的 API 旨在抽象什么以及它旨在公开什么(类似于矢量是一个更好的数组并且旨在公开其数组性质这一事实)。如果您回答这个问题,您应该更好地了解基于迭代器的 API 是否有意义。
I would recommend reading up on Stepanov's design philosophy (pages 9-11):
Stepanov has a lot of other interesting documents available, especially in the "Class Notes" section.
Yes, there are several rules of thumb regarding OOP. No, I'm not convinced that they are really the best way to do things. When you're working with the STL it makes a lot of sense to do things the STL compatible way. And when your abstraction is low level (like
std::vector
, which is meant specifically to make working with dynamically allocated arrays easier; i.e., it should be usable almost like an array with some added features), then some of those OOP rules of thumb make no sense at all.To answer the original question: even beginners will eventually need to learn about iterators, object lifetimes, and what I'll call an object's useful life (i.e., "the object hasn't fallen out of scope, but is no longer valid to use, like an invalidated iterator"). I don't see any reason to try to hide those facts of life from the user, so I personally wouldn't rule out an iterator-based API on those grounds. The real question is what your API is meant to abstract and what's it's meant to expose (similar to the fact that a
vector
is a nicer array and is meant to expose its array nature). If you answer that, you should have a better idea about whether an iterator-based API makes sense.正如 Scott Meyers 在《Effective C++》中所说:是的,通过指针、迭代器或引用授予私有/受保护成员的访问权限确实不是一个好的设计,因为你永远不知道客户端代码会用它做什么。
据我所知,应该避免这种情况,有时最好创建数据成员的副本,然后将其返回给调用者。
As Scott Meyers states in Effective C++: yes it is indeed not a good design to grant access to private/protected members via pointers, iterators or references because you never know what the client code will do with it.
As far as I can remember this should be avoided, and it is sometimes better to create a copy of data members which are then returned to the caller.
这是一个糟糕或错误的实现,而不是设计。
至于通过指针提供对私有或受保护成员的访问,基本上它破坏了
抽象
的基本OOP原则之一。我不确定问题是什么,是的,当然,实现使迭代器无效的实现是不好的。这里真正的Q是什么?
It is a bad or faulty implementation rather than design.
As for providing access to private or protected members through pointers, basically it destroys one of the basic OOP principle of
Abstraction
.I am unsure though as to what the question is, Yes ofcourse it is bad to have implementation which invalidates iterator. What is the real Q here?