0

Effective C++ 的第 28 条说avoid returning "handles" to object internals这个问题展示了如何通过考虑封装来设计你的代码来做到这一点,以避免意外暴露你的类的内部。

我的示例涉及一个数据数组,由于内存是我想避免使用的问题std::vector(和 Boost 库)。

在这里使用数组是我的代码的一个非常简化的版本:

class Foo {
public:
    Foo(int size) : data_(new int[size]) {
        // Populate with data just for the example
        for(int i = 0; i < size; i++) {
            data_[i] = i;
        }
    }

    // I know that this isn't good practice
    int* const get_data() const {
        return data_;
    }

    ~Foo() {
        delete[] data_;
    }

private:
    int* data_;
};

int main(void) {
    Foo* foo = new Foo(10);
    int* const data = foo->get_data();
    delete foo;
    // data now dangles which is bad!
    return 0;
}

我知道使用constwithget_data()并不安全。如果我使用的是向量,我可以像上面的示例问题一样复制它,但是由于我想避免这种情况,我想知道如何最好地设计我的类来避免这种潜在的危险情况?

4

5 回答 5

3

编程和软件设计是一个不变的“选择一件事而不是另一件事”。如果您真的负担不起使用std::vector,并且您需要使用您的类将数据公开给客户端代码,那么就这样吧。记录为什么会这样,并确保随着时间的推移对其进行审查 - 可能是当您的具有 8MB RAM 而不是 2MB 的新硬件模型将能够使用 avector接管您的普通指针的额外 4 个字节。

或者写一个访问器函数:

int& Foo::operator[](int pos)
{
   // Add checks here to ensure `pos` is in range?
   return data[pos]; 
}

现在您根本不必返回指针......

于 2013-04-15T08:35:27.960 回答
1

在您的情况下,一种方法是使用智能指针。我更喜欢shared_ptr。以下是您可以如何修改您的课程以避免您提出的问题:

class Foo {
public:
    Foo(int size) : data_(new int[size]) {
        // Populate with data just for the example
        for(int i = 0; i < size; i++) {
            data_[i] = i;
        }
    }

    // I know that this isn't good practice
    std::shared_ptr<int> const get_data() const {
        return data_;
    }

    ~Foo() {
        //delete[] data_;
    }

private:
    std::shared_ptr<int> data_;
};

int main(void) {
    Foo* foo = new Foo(10);
    std::shared_ptr<int> data = foo->get_data();
    delete foo;
    // data still good )) // --now dangles which is bad!--
    return 0;
}
于 2013-04-15T08:29:06.070 回答
0

如果您真的希望避免使用 std::vector,您可以实现类似于某些 winapi 函数使用的方法。

size_t get_data(int* data_ptr, size_t& buffer_size)
{
if(buffer_size < sizeof (data_)
{
buffer_size = sizeof(data);
return 0;
}
//You could you memcpy for ints here
        for(int i = 0; i < size; i++) {
            data_ptr[i] = i;
        }
return sizof(data_);
}

因此要求调用者提供内存来存储数据并关心该内存的分配。
通过它应该重新发明 std::vector 已经可以做的事情,但安全性要低得多。此外,当您开始更广泛地使用您的课程时,您应该检查规则三以避免出现问题。

于 2013-04-15T08:41:14.260 回答
0

在很多情况下,返回指针是完全可以接受的,即使数组实际上包含在std::vector; 例如,使用外部 API 和复制数据时不受欢迎。

我没有看到危险,因为您不是为猴子编程。类的用户应该知道。但是我添加了查询数据大小的功能。那是为了安全。

extern "C"{
void foobar( const int *data, unsigned int size );
}
class Foo {
public:
    Foo(unsigned int size) : size_(size), data_(new int[size]) {
        // Populate with data just for the example
        for(unsigned int i = 0; i < size; i++) {
            data_[i] = i;
        }
    }

    unsigned int get_size() const{return size_;}
    int* const get_data() const {
        return data_;
    }

    ~Foo() {
        delete[] data_;
    }

private:
    int* data_;
    unsigned int size_;
};

int main(void) {
    Foo* foo = new Foo(10);
    int* const data = foo->get_data();
    foobar( data, foo->get_size() );
    delete foo;
    return 0;
}
于 2013-04-15T09:29:30.320 回答
0

考虑到即使是标准库也有get_native_handle()类,std::thread我想知道标准库或 EC++ 之间谁错了!

有效的 C++ 是一组指导方针,您根本无法在任何上下文中应用它们。这是一本“道德”的书。不要将其视为“合法”。如果您需要公开数据,请执行此操作。

毕竟谁删除了指向您的内部数据的指针(在通过 getter 获取它之后)也是在做“坏事”,因为他正在对不属于他的东西采取破坏性行动。

于 2013-04-15T09:37:59.767 回答