为了账号安全,请及时绑定邮箱和手机立即绑定

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

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

qq_笑_17 2019-08-19 10:33:03
我们能否增加这种面向密钥的访问保护模式的可重用性?我们是否可以增加这种面向密钥的访问保护模式的可重用性:class SomeKey {      friend class Foo;     // more friends... ?     SomeKey() {}      // possibly non-copyable too};class Bar {public:     void protectedMethod(SomeKey); // only friends of SomeKey have access};为了避免持续的误解,这种模式不同于律师 - 客户的习惯用语:它可以比律师 - 客户更简洁(因为它不涉及通过第三类代理)它可以允许授权访问权限...但它对原始类也更具侵入性(每个方法一个虚拟参数)
查看完整描述

3 回答

?
互换的青春

TA贡献1797条经验 获得超6个赞

我喜欢这个成语,它有可能变得更干净,更具表现力。


在标准C ++ 03中,我认为以下方式最容易使用且最通用。(但是没有太大的改进。大多数情况下可以节省自己重复。)因为模板参数不能成为朋友,所以我们必须使用宏来定义passkey:


// define passkey groups

#define EXPAND(pX) pX


#define PASSKEY_1(pKeyname, pFriend1)                             \

        class EXPAND(pKeyname)                                    \

        {                                                         \

        private:                                                  \

            friend EXPAND(pFriend1);                              \

            EXPAND(pKeyname)() {}                                 \

                                                                  \

            EXPAND(pKeyname)(const EXPAND(pKeyname)&);            \

            EXPAND(pKeyname)& operator=(const EXPAND(pKeyname)&); \

        }


#define PASSKEY_2(pKeyname, pFriend1, pFriend2)                   \

        class EXPAND(pKeyname)                                    \

        {                                                         \

        private:                                                  \

            friend EXPAND(pFriend1);                              \

            friend EXPAND(pFriend2);                              \

            EXPAND(pKeyname)() {}                                 \

                                                                  \

            EXPAND(pKeyname)(const EXPAND(pKeyname)&);            \

            EXPAND(pKeyname)& operator=(const EXPAND(pKeyname)&); \

        }

// and so on to some N


//////////////////////////////////////////////////////////

// test!

//////////////////////////////////////////////////////////

struct bar;

struct baz;

struct qux;

void quux(int, double);


struct foo

{

    PASSKEY_1(restricted1_key, struct bar);

    PASSKEY_2(restricted2_key, struct bar, struct baz);

    PASSKEY_1(restricted3_key, void quux(int, double));


    void restricted1(restricted1_key) {}

    void restricted2(restricted2_key) {}

    void restricted3(restricted3_key) {}

} f;


struct bar

{

    void run(void)

    {

        // passkey works

        f.restricted1(foo::restricted1_key());

        f.restricted2(foo::restricted2_key());

    }

};


struct baz

{

    void run(void)

    {

        // cannot create passkey

        /* f.restricted1(foo::restricted1_key()); */


        // passkey works

        f.restricted2(foo::restricted2_key());

    }

};


struct qux

{

    void run(void)

    {

        // cannot create any required passkeys

        /* f.restricted1(foo::restricted1_key()); */

        /* f.restricted2(foo::restricted2_key()); */

    }

};


void quux(int, double)

{

    // passkey words

    f.restricted3(foo::restricted3_key());

}


void corge(void)

{

    // cannot use quux's passkey

    /* 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:

    friend T; // C++0x, MSVC allows as extension

    passkey() {}


    // noncopyable

    passkey(const passkey&) = delete;

    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 

// this by creating a tag and specializing 

// 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, ...)               \

        struct EXPAND(pTag);                             \

                                                         \

        template <>                                      \

        class passkey<EXPAND(pTag)>                      \

        {                                                \

        private:                                         \

            friend pFunc __VA_ARGS__;                    \

            passkey() {}                                 \

                                                         \

            passkey(const passkey&) = delete;            \

            passkey& operator=(const passkey&) = delete; \

        }


// meta function determines if a type 

// is contained in a parameter pack

template<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 passkeys

template <typename... Keys>

class allow

{

public:

    // check if passkey is allowed

    template <typename Key>

    allow(const passkey<Key>&)

    {

        static_assert(is_contained<Key, Keys>::value, 

                        "Passkey is not allowed.");

    }


private:

    // noncopyable

    allow(const allow&) = delete;

    allow& operator=(const allow&) = delete;

};


//////////////////////////////////////////////////////////

// test!

//////////////////////////////////////////////////////////

struct bar;

struct baz;

struct qux;

void quux(int, double);


// make a passkey for quux function

PASSKEY_FUNCTION(quux_tag, void quux(int, double));


struct foo

{    

    void restricted1(allow<bar>) {}

    void restricted2(allow<bar, baz>) {}

    void restricted3(allow<quux_tag>) {}

} f;


struct bar

{

    void run(void)

    {

        // passkey works

        f.restricted1(passkey<bar>());

        f.restricted2(passkey<bar>());

    }

};


struct baz

{

    void run(void)

    {

        // passkey does not work

        /* f.restricted1(passkey<baz>()); */


        // passkey works

        f.restricted2(passkey<baz>());

    }

};


struct qux

{

    void run(void)

    {

        // own passkey does not work,

        // cannot create any required passkeys

        /* f.restricted1(passkey<qux>()); */

        /* f.restricted2(passkey<qux>()); */

        /* f.restricted1(passkey<bar>()); */

        /* f.restricted2(passkey<baz>()); */

    }

};


void quux(int, double)

{

    // passkey words

    f.restricted3(passkey<quux_tag>());

}


void corge(void)

{

    // cannot use quux's passkey

    /* f.restricted3(passkey<quux_tag>()); */

}


int main(){}

仅注意样板代码,在大多数情况下(所有非功能情况!)都不需要特别定义。该代码通常简单地为类和函数的任何组合实现惯用语。


调用者不需要尝试创建或记住特定于该函数的密钥。相反,每个类现在都有自己唯一的密码,函数只需在passkey参数的模板参数中选择允许的密钥(不需要额外的定义); 这消除了这两个缺点。调用者只是创建自己的密钥并使用它调用,而不需要担心其他任何事情。


查看完整回答
反对 回复 2019-08-19
  • 3 回答
  • 0 关注
  • 510 浏览
慕课专栏
更多

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信