6

将所有虚函数都设置为私有或受保护的基类接口是有充分理由的(请参阅this)。但是,如何防止派生类(可能在外部客户手中)将私有虚函数设为公有?在Virtually Yours中,作者讨论了这个问题,但没有讨论解决方案。

编辑:从你的回答和我之前的想法来看,似乎没有办法阻止这种情况。但是由于在这种情况下很容易出错(客户端肯定会触及受保护的虚函数),因此编译器会警告这种用法是有道理的。我尝试用 g++ 对其进行测试。首先,我写道:

class A {
        protected:
        virtual void none() { return; }
};

class B: public A {
        public:
        void none() { return; }
};

g++ -c -Wall -pedantic file.cpp编译没有错误。添加-Weffc++给出了警告:warning: ‘class A’ has virtual functions and accessible non-virtual destructor,这是有道理的。添加虚拟析构函数后,没有任何警告。因此,对于这种容易出错的情况,没有任何警告。

4

6 回答 6

13

As Bjarne put it, the access control in C++ is meant to protect against Murphy, not Machiavelli. The same is true in general -- it's features are meant to protect against accidents, not people intentionally doing something wrong.

To an extent, using C++ means putting at least some degree of trust in the other people who will have access to your source code. If they want to badly enough, they can screw things up in all sorts of ways, and you can't do much of anything to stop them. If you want to place real restrictions on how your code is used, C++ is the wrong language for your purposes.

Edit: This isn't really an "argument" at all -- it's simply pointing out the basis upon which decisions were made. Since I have my copy of the D&E out from answering a previous question, I'll type a bit more if it in here1:

It is more important to allow a useful feature than to prevent every misuse: You can write bad programs in any language. It is important to minimize the chance of accidental misuse of features, and much effort has been spent trying to ensure that the default behavior of C++ constructs is either sensible or leads to compile-time errors. For example by default all function argument types are checked -- even across separate compilation boundaries -- and by default all class members are private. However, a systems programming language cannot prevent a determined programmer from break the system so design effort is better expended on providing facilities for writing good programs than preventing the inevitable bad ones. In the longer run, programmers seem to learn. This is a variant of the old C "trust the programmer" slogan. The various type checking and access control rules exist to allow a class provider to state clearly what is expected from users, to protect against accidents. Those rules are not intended as protection against deliberate violation (§2.10).

In §2.10, he says, among other things:

The task of the protection system is to make sure that any such violation of the type system is explicit and to minimize the need for such violations.

Those goals appear to have been met here -- publicizing a protected base class member definitely requires explicit action in the derived class, and in 20+ years of writing C++ I can't remember ever needing (or even wanting) to do it.

1§4.3, pgs. 115, 116.

于 2010-01-27T01:36:09.410 回答
2

You can't. "virtualness" of a function and access type are two different unrelated concepts.

于 2010-01-27T01:19:38.867 回答
2

Access control in C++ possibly doesn't do what you want. It's not intended to enforce DRM-style constraints to stop you sharing your access. If A has access to B, then A can call B and use the result for any purpose, including returning it to another caller who doesn't have access to B.

The problem that's discussed in the article you link to isn't about A deliberately or maliciously sharing B. It's about what happens if you put a public virtual function in a published interface, and later try to change the class so that it uses their suggested Template Method patterns, including private virtual functions. Child classes have written public overrides of the virtual function, so you can no longer separate the two concerns (access and virtual-ness) without modifying all the child classes. The way I read it, the article does provide a solution to the problem it presents, and that solution is "never make virtual functions public in the first place".

Virtual functions should be treated very much like data members — make them private, until design needs indicate a less restricted approach is indicated. It is much easier to promote them to a more accessible level, than it is to demote them to a more private level.

The reason this doesn't solve your problem, is that they didn't consider your problem.

于 2010-01-27T01:32:16.573 回答
2

在派生类中将私有/受保护的虚拟方法提升为公共方法不会公开基类方法。它仍然不能通过基类指针调用。它不会成为基类接口的一部分。

于 2010-01-27T01:50:08.693 回答
2

编译器会警告这种用法是有道理的。

为什么会呢?由每个类的设计者决定其外部接口。

在 C++ 中,基类没有强制执行派生类接口属性的特殊能力。当基函数是公共的时,派生类可以决定将某些覆盖函数设为私有,反之亦然。派生类的接口是与其客户的契约,而不是与基类的契约(除非基类是派生类的客户,就像奇怪地重复出现的模板基类一样)。

于 2011-12-26T01:58:25.197 回答
1

您可能需要一个只能由派生类型构造的令牌参数。当然,他们可以只公开令牌的子类。所以你必须给它一个虚拟析构函数并 RTTI 检查它。

protected:
class ProtectedToken { virtual ~ProtectedToken() { } };
virtual void my_tough_cookie(int arg,
  ProtectedToken const &tok = ProtectedToken() ) {
    assert ( typeid( tok ) == typeid( ProtectedToken ) );
    …
}

当然,这对任何人都不是一件好事,包括你自己。

编辑:呸,它不起作用。即使确实如此,您也可以那样做public: using Base::ProtectedToken并击败保护。又浪费了我生命中的 15 分钟……</p>

于 2010-01-27T02:31:54.603 回答