猿问
下载APP

我们能否增加这种面向密钥的访问保护模式的可重用性?

我们能否增加这种面向密钥的访问保护模式的可重用性?

我们是否可以增加这种面向密钥的访问保护模式的可重用性:

class SomeKey { 
    friend class Foo;
    // more friends... ?
    SomeKey() {} 
    // possibly non-copyable too};class Bar {public:
    void protectedMethod(SomeKey); // only friends of SomeKey have access};

为了避免持续的误解,这种模式不同于律师 - 客户的习惯用语:

  • 它可以比律师 - 客户更简洁(因为它不涉及通过第三类代理)

  • 它可以允许授权访问权限

  • ...但它对原始类也更具侵入性(每个方法一个虚拟参数)


qq_笑_17
浏览 41回答 3
3回答

互换的青春

我喜欢这个成语,它有可能变得更干净,更具表现力。在标准C ++ 03中,我认为以下方式最容易使用且最通用。(但是没有太大的改进。大多数情况下可以节省自己重复。)因为模板参数不能成为朋友,所以我们必须使用宏来定义passkey:// define passkey groups#define EXPAND(pX) pX#define PASSKEY_1(pKeyname, pFriend1)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;\&nbsp; &nbsp; &nbsp; &nbsp; class EXPAND(pKeyname)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; \&nbsp; &nbsp; &nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;\&nbsp; &nbsp; &nbsp; &nbsp; private:&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; \&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; friend EXPAND(pFriend1);&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; \&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; EXPAND(pKeyname)() {}&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;\&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; \&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; EXPAND(pKeyname)(const EXPAND(pKeyname)&);&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; \&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; EXPAND(pKeyname)& operator=(const EXPAND(pKeyname)&); \&nbsp; &nbsp; &nbsp; &nbsp; }#define PASSKEY_2(pKeyname, pFriend1, pFriend2)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;\&nbsp; &nbsp; &nbsp; &nbsp; class EXPAND(pKeyname)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; \&nbsp; &nbsp; &nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;\&nbsp; &nbsp; &nbsp; &nbsp; private:&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; \&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; friend EXPAND(pFriend1);&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; \&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; friend EXPAND(pFriend2);&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; \&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; EXPAND(pKeyname)() {}&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;\&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; \&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; EXPAND(pKeyname)(const EXPAND(pKeyname)&);&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; \&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; EXPAND(pKeyname)& operator=(const EXPAND(pKeyname)&); \&nbsp; &nbsp; &nbsp; &nbsp; }// and so on to some N//////////////////////////////////////////////////////////// test!//////////////////////////////////////////////////////////struct bar;struct baz;struct qux;void quux(int, double);struct foo{&nbsp; &nbsp; PASSKEY_1(restricted1_key, struct bar);&nbsp; &nbsp; PASSKEY_2(restricted2_key, struct bar, struct baz);&nbsp; &nbsp; PASSKEY_1(restricted3_key, void quux(int, double));&nbsp; &nbsp; void restricted1(restricted1_key) {}&nbsp; &nbsp; void restricted2(restricted2_key) {}&nbsp; &nbsp; void restricted3(restricted3_key) {}} f;struct bar{&nbsp; &nbsp; void run(void)&nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; // passkey works&nbsp; &nbsp; &nbsp; &nbsp; f.restricted1(foo::restricted1_key());&nbsp; &nbsp; &nbsp; &nbsp; f.restricted2(foo::restricted2_key());&nbsp; &nbsp; }};struct baz{&nbsp; &nbsp; void run(void)&nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; // cannot create passkey&nbsp; &nbsp; &nbsp; &nbsp; /* f.restricted1(foo::restricted1_key()); */&nbsp; &nbsp; &nbsp; &nbsp; // passkey works&nbsp; &nbsp; &nbsp; &nbsp; f.restricted2(foo::restricted2_key());&nbsp; &nbsp; }};struct qux{&nbsp; &nbsp; void run(void)&nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; // cannot create any required passkeys&nbsp; &nbsp; &nbsp; &nbsp; /* f.restricted1(foo::restricted1_key()); */&nbsp; &nbsp; &nbsp; &nbsp; /* f.restricted2(foo::restricted2_key()); */&nbsp; &nbsp; }};void quux(int, double){&nbsp; &nbsp; // passkey words&nbsp; &nbsp; f.restricted3(foo::restricted3_key());}void corge(void){&nbsp; &nbsp; // cannot use quux's passkey&nbsp; &nbsp; /* f.restricted3(foo::restricted3_key()); */}int main(){}此方法有两个缺点:1)调用者必须知道它需要创建的特定密钥。虽然简单的命名方案(function_key)基本上消除了它,但它仍然可以是一个抽象清洁器(并且更容易)。2)虽然使用宏并不是很困难,但可能会看起来有点难看,需要一组passkey-definitions。但是,在C ++ 03中无法对这些缺点进行改进。在C ++ 0x中,成语可以达到其最简单,最具表现力的形式。这是由于可变参数模板和允许模板参数成为朋友。(请注意,2010年之前的MSVC允许模板专家说明符作为扩展;因此可以模拟此解决方案):// each class has its own unique key only it can create// (it will try to get friendship by "showing" its passkey)template <typename T>class passkey{private:&nbsp; &nbsp; friend T; // C++0x, MSVC allows as extension&nbsp; &nbsp; passkey() {}&nbsp; &nbsp; // noncopyable&nbsp; &nbsp; passkey(const passkey&) = delete;&nbsp; &nbsp; passkey& operator=(const passkey&) = delete;};// functions still require a macro. this// is because a friend function requires// the entire declaration, which is not// just a type, but a name as well. we do&nbsp;// this by creating a tag and specializing&nbsp;// the passkey for it, friending the function#define EXPAND(pX) pX// we use variadic macro parameters to allow// functions with commas, it all gets pasted// back together again when we friend it#define PASSKEY_FUNCTION(pTag, pFunc, ...)&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;\&nbsp; &nbsp; &nbsp; &nbsp; struct EXPAND(pTag);&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;\&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;\&nbsp; &nbsp; &nbsp; &nbsp; template <>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; \&nbsp; &nbsp; &nbsp; &nbsp; class passkey<EXPAND(pTag)>&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; \&nbsp; &nbsp; &nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; \&nbsp; &nbsp; &nbsp; &nbsp; private:&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;\&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; friend pFunc __VA_ARGS__;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; \&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; passkey() {}&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;\&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;\&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; passkey(const passkey&) = delete;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; \&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; passkey& operator=(const passkey&) = delete; \&nbsp; &nbsp; &nbsp; &nbsp; }// meta function determines if a type&nbsp;// is contained in a parameter packtemplate<typename T, typename... List>struct is_contained : std::false_type {};template<typename T, typename... List>struct is_contained<T, T, List...> : std::true_type {};template<typename T, typename Head, typename... List>struct is_contained<T, Head, List...> : is_contained<T, List...> {};// this class can only be created with allowed passkeystemplate <typename... Keys>class allow{public:&nbsp; &nbsp; // check if passkey is allowed&nbsp; &nbsp; template <typename Key>&nbsp; &nbsp; allow(const passkey<Key>&)&nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; static_assert(is_contained<Key, Keys>::value,&nbsp;&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; "Passkey is not allowed.");&nbsp; &nbsp; }private:&nbsp; &nbsp; // noncopyable&nbsp; &nbsp; allow(const allow&) = delete;&nbsp; &nbsp; allow& operator=(const allow&) = delete;};//////////////////////////////////////////////////////////// test!//////////////////////////////////////////////////////////struct bar;struct baz;struct qux;void quux(int, double);// make a passkey for quux functionPASSKEY_FUNCTION(quux_tag, void quux(int, double));struct foo{&nbsp; &nbsp;&nbsp;&nbsp; &nbsp; void restricted1(allow<bar>) {}&nbsp; &nbsp; void restricted2(allow<bar, baz>) {}&nbsp; &nbsp; void restricted3(allow<quux_tag>) {}} f;struct bar{&nbsp; &nbsp; void run(void)&nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; // passkey works&nbsp; &nbsp; &nbsp; &nbsp; f.restricted1(passkey<bar>());&nbsp; &nbsp; &nbsp; &nbsp; f.restricted2(passkey<bar>());&nbsp; &nbsp; }};struct baz{&nbsp; &nbsp; void run(void)&nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; // passkey does not work&nbsp; &nbsp; &nbsp; &nbsp; /* f.restricted1(passkey<baz>()); */&nbsp; &nbsp; &nbsp; &nbsp; // passkey works&nbsp; &nbsp; &nbsp; &nbsp; f.restricted2(passkey<baz>());&nbsp; &nbsp; }};struct qux{&nbsp; &nbsp; void run(void)&nbsp; &nbsp; {&nbsp; &nbsp; &nbsp; &nbsp; // own passkey does not work,&nbsp; &nbsp; &nbsp; &nbsp; // cannot create any required passkeys&nbsp; &nbsp; &nbsp; &nbsp; /* f.restricted1(passkey<qux>()); */&nbsp; &nbsp; &nbsp; &nbsp; /* f.restricted2(passkey<qux>()); */&nbsp; &nbsp; &nbsp; &nbsp; /* f.restricted1(passkey<bar>()); */&nbsp; &nbsp; &nbsp; &nbsp; /* f.restricted2(passkey<baz>()); */&nbsp; &nbsp; }};void quux(int, double){&nbsp; &nbsp; // passkey words&nbsp; &nbsp; f.restricted3(passkey<quux_tag>());}void corge(void){&nbsp; &nbsp; // cannot use quux's passkey&nbsp; &nbsp; /* f.restricted3(passkey<quux_tag>()); */}int main(){}仅注意样板代码,在大多数情况下(所有非功能情况!)都不需要特别定义。该代码通常简单地为类和函数的任何组合实现惯用语。调用者不需要尝试创建或记住特定于该函数的密钥。相反,每个类现在都有自己唯一的密码,函数只需在passkey参数的模板参数中选择允许的密钥(不需要额外的定义); 这消除了这两个缺点。调用者只是创建自己的密钥并使用它调用,而不需要担心其他任何事情。

萧十郎

我读过很多关于不可复制性的评论。许多人认为它不应该是不可复制的,因为我们不能将它作为参数传递给需要密钥的函数。有些人甚至对此感到惊讶。好吧它真的不应该,并且显然与一些Visual C ++编译器有关,因为我之前有相同的怪异但不再使用Visual C ++ 12(Studio 2013)。但事实上,我们可以通过“基本”不可复制性来增强安全性。Boost版本太多了,因为它完全阻止了复制构造函数的使用,因此对于我们需要的东西来说有点太多了。我们需要的是实际上将复制构造函数设为私有但不是没有实现。当然,实现将是空的,但它必须存在。我最近询问过谁在这种情况下调用copy-ctor(在这种情况下调用SomeKey时调用复制构造函数ProtectedMethod)。答案是,显然标准确保它是方法调用者,它称之为-ctor诚实看起来非常合乎逻辑。所以通过copy-ctor私有我们允许朋友的功能(protected&nbsp;Bar和granted&nbsp;Foo)来调用它,从而允许Foo调用ProtectedMethod因为它使用值参数传递,但它也阻止任何人超出Foo范围。通过这样做,即使同事开发人员试图巧妙地使用代码,他实际上也必须Foo完成工作,另一个课程将无法获得密钥,并且他很可能几乎100%意识到他的错误这种方式的时间(希望,否则他太多的初学者使用这种模式或他应该停止发展:P)。

largeQ

来自@GManNickG的精彩回答。学到了很多东西。试图让它工作,发现了一些错别字。为清楚起见,重复完整示例 我的示例借用了“包含Key in Keys ...”函数来检查C ++ 0x参数包是否包含 @snk_kid发布的类型。#include<type_traits>#include<iostream>// identify if type is in a parameter pack or nottemplate < typename Tp, typename... List >struct contains : std::false_type {};template < typename Tp, typename Head, typename... Rest >struct contains<Tp, Head, Rest...> :&nbsp; std::conditional< std::is_same<Tp, Head>::value,&nbsp; std::true_type,&nbsp; contains<Tp, Rest...>&nbsp; >::type{};template < typename Tp >struct contains<Tp> : std::false_type{};// everything is private!template <typename T>class passkey {private:&nbsp; friend T;&nbsp; passkey() {}&nbsp; // noncopyable&nbsp; passkey(const passkey&) = delete;&nbsp; passkey& operator=(const passkey&) = delete;};// what keys are allowedtemplate <typename... Keys>class allow {public:&nbsp; template <typename Key>&nbsp; allow(const passkey<Key>&) {&nbsp; &nbsp; static_assert(contains<Key, Keys...>::value, "Pass key is not allowed");&nbsp; }private:&nbsp; // noncopyable&nbsp; allow(const allow&) = delete;&nbsp; allow& operator=(const allow&) = delete;};struct for1;struct for2;struct foo {&nbsp; void restrict1(allow<for1>) {}&nbsp; void restrict2(allow<for1, for2>){}} foo1;struct for1 {&nbsp; void myFnc() {&nbsp; &nbsp; foo1.restrict1(passkey<for1>());&nbsp; }};struct for2 {&nbsp; void myFnc() {&nbsp; &nbsp; foo1.restrict2(passkey<for2>());&nbsp; &nbsp;// foo1.restrict1(passkey<for2>()); // no passkey&nbsp; }};void main() {&nbsp; std::cout << contains<int, int>::value << std::endl;&nbsp; std::cout << contains<int>::value << std::endl;&nbsp; std::cout << contains<int, double, bool, unsigned int>::value << std::endl;&nbsp; std::cout << contains<int, double>::value << std::endl;}
打开App,查看更多内容
随时随地看视频慕课网APP
我要回答