【问题标题】:Secure direct access to class members安全地直接访问班级成员
【发布时间】:2012-10-06 02:39:28
【问题描述】:

我想知道以下语法是否可以“接受”,或者良好的做法是否认为这是来自地狱。目标是增加一定程度的保护,以迫使开发人员清楚地意识到自己在做什么。这是语法:

class MyClass
{
    public:
        template<bool RemoveProtection = false> 
        inline std::ofstream& writeStream()
        {
            static_assert(RemoveProtection, "You're doing it wrong");
            return _writeStream;
        }

        inline const std::ofstream& writeStream() const
        {
            return _writeStream;
        }

    protected:
        std::ofstream _writeStream;
};

用途是:

x.writeStream().good(); // <- OK
x.writeStream().put('c'); // <- NOT OK
x.writeStream<true>().put('c'); // <- OK

我发现这是告诉开发人员的一种便捷方式:“当心,您使用的是低级函数,您必须小心您正在做的事情”。以某种“保护”方式为班级成员提供直接访问权限是一种“可接受的”方式吗?还有其他编码方式吗?

【问题讨论】:

  • 你让你的代码变得丑陋、难以维护和不方便......究竟是什么?定义你的界面。那是你的类的接口。不要让开发人员通过使用一些荒谬的模板标志黑客来绕过它。如果您正在编写代码,您总是必须知道自己在做什么。必须显式输入&lt;true&gt; 以表明您尤其知道自己在做什么,这只是……非常非常错误。开发人员有文档。他们不需要训练轮和人为的限制,他们需要清晰简洁的代码来让他们完成工作。

标签: c++ templates constants getter


【解决方案1】:

看看meagar's comment

您正在使您的代码变得丑陋、难以维护并且不方便……究竟是什么?定义你的界面。那是你的类的接口。不要让开发人员通过使用一些荒谬的模板标志黑客来绕过它。如果你正在编写代码,你总是必须知道你在做什么。必须显式键入&lt;true&gt; 以表明您特别知道自己在做什么,这只是……非常非常错误。开发人员有文档。他们不需要训练轮和人为的限制,他们需要清晰简洁的代码来让他们完成工作。 – meagar 2012-10-06 02:41:53Z

当其他用户使用它时,您提供给其他人的课程永远不会进入不可预测的状态。在这种情况下,不可预测的状态是您在编写课程时从未考虑过的状态。因此,您应该永远不允许访问类的低级方法或记录可能的缺陷

假设您正在编写一个记录器:

struct MyLogger{
    MyLogger(std::string filename) : stream(filename.c_str()){}      
    template <typename T>
    MyLogger& operator<<(const T& v){ stream << v << " "; return *this;}

private:
    std::ofstream stream;
};

忽略没有复制构造函数并且缺少赋值操作数。也忽略它是一个粗略的记录器,它甚至不提供时间戳。但是,如您所见,记录器的状态完全取决于记录器的方法,例如如果文件已成功打开,则在记录器被销毁之前不会关闭。

现在说我们使用你的方法:

struct MyLogger{
    MyLogger(std::string filename) : stream(filename.c_str()){}
    template <typename T>
    MyLogger& operator<<(const T& v){ stream << v << " "; return *this;}

    template<bool RemoveProtection = false> 
    inline std::ofstream& writeStream()
    {
        static_assert(RemoveProtection, "You're doing it wrong");
        return stream;
    }

    inline const std::ofstream& writeStream() const
    {
        return stream;
    }

private:
    std::ofstream stream;
};

现在有人使用下面的代码

logger.writeStream<true>.close();

砰。你的记录器坏了。当然是用户的错,因为他们使用了&lt;true&gt;,不是吗?但用户通常会复制示例代码,尤其是当他第一次使用库时。用户看到你的例子

logger.writeStream().good(); // <- OK
logger.writeStream().put('c'); // <- NOT OK
logger.writeStream<true>().put('c'); // <- OK

一开始完全忽略了文档。然后他将使用第一个和最后一个版本。后来他发现最后一个版本每次都有效! &lt;true&gt; 是多么神奇的事情啊。然后他开始指责你发生了邪恶的事情,所以你必须通过包含警告的文档来保护自己免受公然的火焰:

    /**
    * \brief Returns a reference to the internal write stream
    *
    * \note You have to use the template parameter `<true>` in order to use it
    *
    * \warning Please note that this will return a reference to the internal 
    *          write stream. As such you shouldn't create any state in which 
    *          the logger cannot work, such as closing the stream.        
    */
    template<bool RemoveProtection = false> 
    inline std::ofstream& writeStream()
    {
        static_assert(RemoveProtection, "You're doing it wrong");
        return stream;
    }

那么,我们得到了什么?我们仍然不得不把这个警告放在某个地方。如果我们将 stream 设为 public 会简单得多:

struct MyLogger{
    MyLogger(std::string filename) : stream(filename.c_str()){}
    template <typename T>
    MyLogger& operator<<(const T& v){ stream << v << " "; return *this;}

    /**
    * The internal write stream. Please look out that you don't create
    * any state in which the logger cannot work, such as closing the stream.
    */
    std::ofstream stream;
};

还是坚持

/** >put warning here< */
inline std::ofstream & writeStream()
{
    return stream;
}

哎呀。因此,要么不允许访问您的低级方法(如果应该允许它们使用它们,则为特定的std::ofstream 方法构建一个包装器)或者记录如果您对对象的内部进行大量更改时可能发生的缺陷,但是不要中途使用static_assert 让它看起来不错。

【讨论】:

    猜你喜欢
    • 2011-08-09
    • 1970-01-01
    • 1970-01-01
    • 2022-01-10
    • 1970-01-01
    • 2020-09-11
    • 1970-01-01
    • 1970-01-01
    • 2013-05-26
    相关资源
    最近更新 更多