取消引用指针来获取引用是错误的吗?

发布于 2024-09-13 18:42:21 字数 264 浏览 3 评论 0原文

我更喜欢在任何地方都使用引用,但是当您使用 STL 容器时,您必须使用指针,除非您确实想按值传递复杂类型。我觉得转换回参考很肮脏,这似乎是错误的。

是吗?

澄清一下......

MyType *pObj = ...
MyType &obj = *pObj;

这不是“脏”吗,因为你可以(即使只是在理论上,因为你首先检查它)取消引用 NULL 指针?

编辑:哦,你不知道对象是否是动态创建的。

I'd much prefer to use references everywhere but the moment you use an STL container you have to use pointers unless you really want to pass complex types by value. And I feel dirty converting back to a reference, it just seems wrong.

Is it?

To clarify...

MyType *pObj = ...
MyType &obj = *pObj;

Isn't this 'dirty', since you can (even if only in theory since you'd check it first) dereference a NULL pointer?

EDIT: Oh, and you don't know if the objects were dynamically created or not.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

浅浅 2024-09-20 18:42:21

在尝试将指针转换为引用之前,请确保指针不为 NULL,并且只要引用存在,该对象就会保留在范围内(或保持分配状态,引用堆),这样就可以了,而且道德清白:)

Ensure that the pointer is not NULL before you try to convert the pointer to a reference, and that the object will remain in scope as long as your reference does (or remain allocated, in reference to the heap), and you'll be okay, and morally clean :)

还如梦归 2024-09-20 18:42:21

使用取消引用的指针初始化引用绝对没问题,没有任何问题。如果 p 是一个指针,并且解除引用它是有效的(例如,它不为 null),那么 *p 就是它指向的对象。您可以绑定对该对象的引用,就像将引用绑定到任何对象一样。显然,您必须确保引用不会比对象活得更久(就像任何引用一样)。

例如,假设我传递了一个指向对象数组的指针。它也可以是迭代器对、对象向量或对象map,但为了简单起见,我将使用数组。每个对象都有一个函数 order,返回一个整数。我要在每个对象上调用一次 bar 函数,按照 order 值递增的顺序:

void bar(Foo &f) {
    // does something
}

bool by_order(Foo *lhs, Foo *rhs) {
    return lhs->order() < rhs->order();
}

void call_bar_in_order(Foo *array, int count) {
    std::vector<Foo*> vec(count);  // vector of pointers
    for (int i = 0; i < count; ++i) vec[i] = &(array[i]);
    std::sort(vec.begin(), vec.end(), by_order);
    for (int i = 0; i < count; ++i) bar(*vec[i]); 
}

我的示例初始化的引用是函数参数而不是直接变量,但我可以有效地完成:

for (int i = 0; i < count; ++i) {
    Foo &f = *vec[i];
    bar(f);
}

显然 vector 是不正确的,因为那时我将在 copy 上调用 bar每个对象按顺序排列,而不是每个对象按顺序排列。 bar 采用非常量引用,因此除了性能或其他任何因素之外,如果 bar 修改输入,这显然是错误的。

智能指针向量或 boost 指针向量也将是错误的,因为我不拥有数组中的对象,并且当然不能释放它们。也可能不允许对原始数组进行排序,或者如果它是 map 而不是数组,则不可能对原始数组进行排序。

Initialising a reference with a dereferenced pointer is absolutely fine, nothing wrong with it whatsoever. If p is a pointer, and if dereferencing it is valid (so it's not null, for instance), then *p is the object it points to. You can bind a reference to that object just like you bind a reference to any object. Obviously, you must make sure the reference doesn't outlive the object (like any reference).

So for example, suppose that I am passed a pointer to an array of objects. It could just as well be an iterator pair, or a vector of objects, or a map of objects, but I'll use an array for simplicity. Each object has a function, order, returning an integer. I am to call the bar function once on each object, in order of increasing order value:

void bar(Foo &f) {
    // does something
}

bool by_order(Foo *lhs, Foo *rhs) {
    return lhs->order() < rhs->order();
}

void call_bar_in_order(Foo *array, int count) {
    std::vector<Foo*> vec(count);  // vector of pointers
    for (int i = 0; i < count; ++i) vec[i] = &(array[i]);
    std::sort(vec.begin(), vec.end(), by_order);
    for (int i = 0; i < count; ++i) bar(*vec[i]); 
}

The reference that my example has initialized is a function parameter rather than a variable directly, but I could just have validly done:

for (int i = 0; i < count; ++i) {
    Foo &f = *vec[i];
    bar(f);
}

Obviously a vector<Foo> would be incorrect, since then I would be calling bar on a copy of each object in order, not on each object in order. bar takes a non-const reference, so quite aside from performance or anything else, that clearly would be wrong if bar modifies the input.

A vector of smart pointers, or a boost pointer vector, would also be wrong, since I don't own the objects in the array and certainly must not free them. Sorting the original array might also be disallowed, or for that matter impossible if it's a map rather than an array.

ヤ经典坏疍 2024-09-20 18:42:21

否。您还能如何实现 operator=?您必须取消引用 this 才能返回对您自己的引用。

请注意,尽管我仍然按值将项目存储在 STL 容器中 - 除非您的对象很大,否则堆分配的开销将意味着您使用更多的存储空间,并且效率较低。只是按值存储该项目。

No. How else could you implement operator=? You have to dereference this in order to return a reference to yourself.

Note though that I'd still store the items in the STL container by value -- unless your object is huge, overhead of heap allocations is going to mean you're using more storage, and are less efficient, than you would be if you just stored the item by value.

同尘 2024-09-20 18:42:21

我更喜欢在任何地方都使用引用,但是当你使用 STL 容器时,你必须使用指针,除非你真的想按值传递复杂类型。

需要明确的是:STL 容器被设计为支持某些语义(“值语义”),例如“容器中的项目可以复制”。由于引用不可重新绑定,因此它们不支持值语义(即,尝试创建 std::vectorstd::list< /代码>)。您不能将引用放入 STL 容器,这是正确的。

一般来说,如果您使用引用而不是普通对象,那么您要么使用基类并希望避免切片,要么尝试避免复制。是的,这意味着如果您想将项目存储在 STL 容器中,那么您将需要使用指针来避免切片和/或复制。

并且,是的,以下是合法的(尽管在这种情况下,不是很有用):

#include <iostream>
#include <vector>

// note signature, inside this function, i is an int&
// normally I would pass a const reference, but you can't add
// a "const* int" to a "std::vector<int*>"
void add_to_vector(std::vector<int*>& v, int& i)
{
    v.push_back(&i);
}

int main()
{
    int x = 5;
    std::vector<int*> pointers_to_ints;

    // x is passed by reference
    // NOTE:  this line could have simply been "pointers_to_ints.push_back(&x)"
    // I simply wanted to demonstrate (in the body of add_to_vector) that
    // taking the address of a reference returns the address of the object the
    // reference refers to.
    add_to_vector(pointers_to_ints, x);

    // get the pointer to x out of the container
    int* pointer_to_x = pointers_to_ints[0];

    // dereference the pointer and initialize a reference with it
    int& ref_to_x = *pointer_to_x;

    // use the reference to change the original value (in this case, to change x)
    ref_to_x = 42;

    // show that x changed
    std::cout << x << '\n';
}

哦,你不知道这些对象是否是动态创建的。

那不重要。在上面的示例中,x 位于堆栈上,我们将指向 x 的指针存储在 pointers_to_vectors 中。当然,pointers_to_vectors 在内部使用动态分配的数组(当向量超出范围时删除该数组),但是数组保存的是指针,而不是指向的东西。当 pointers_to_ints 超出范围时,内部 int*[] 会被 delete[] 删除,但 int* code> 不能删除

事实上,这使得在 STL 容器中使用指针变得很困难,因为 STL 容器不会管理所指向对象的生命周期。您可能想查看 Boost 的指针容器库。否则,您要么 (1) 使用智能指针的 STL 容器(例如对于 STL 容器来说是合法的 boost:shared_ptr),要么 (2) 管理所指向对象的生命周期其他方式。您可能已经在做 (2)。

I'd much prefer to use references everywhere but the moment you use an STL container you have to use pointers unless you really want to pass complex types by value.

Just to be clear: STL containers were designed to support certain semantics ("value semantics"), such as "items in the container can be copied around." Since references aren't rebindable, they don't support value semantics (i.e., try creating a std::vector<int&> or std::list<double&>). You are correct that you cannot put references in STL containers.

Generally, if you're using references instead of plain objects you're either using base classes and want to avoid slicing, or you're trying to avoid copying. And, yes, this means that if you want to store the items in an STL container, then you're going to need to use pointers to avoid slicing and/or copying.

And, yes, the following is legit (although in this case, not very useful):

#include <iostream>
#include <vector>

// note signature, inside this function, i is an int&
// normally I would pass a const reference, but you can't add
// a "const* int" to a "std::vector<int*>"
void add_to_vector(std::vector<int*>& v, int& i)
{
    v.push_back(&i);
}

int main()
{
    int x = 5;
    std::vector<int*> pointers_to_ints;

    // x is passed by reference
    // NOTE:  this line could have simply been "pointers_to_ints.push_back(&x)"
    // I simply wanted to demonstrate (in the body of add_to_vector) that
    // taking the address of a reference returns the address of the object the
    // reference refers to.
    add_to_vector(pointers_to_ints, x);

    // get the pointer to x out of the container
    int* pointer_to_x = pointers_to_ints[0];

    // dereference the pointer and initialize a reference with it
    int& ref_to_x = *pointer_to_x;

    // use the reference to change the original value (in this case, to change x)
    ref_to_x = 42;

    // show that x changed
    std::cout << x << '\n';
}

Oh, and you don't know if the objects were dynamically created or not.

That's not important. In the above sample, x is on the stack and we store a pointer to x in the pointers_to_vectors. Sure, pointers_to_vectors uses a dynamically-allocated array internally (and delete[]s that array when the vector goes out of scope), but that array holds the pointers, not the pointed-to things. When pointers_to_ints falls out of scope, the internal int*[] is delete[]-ed, but the int*s are not deleted.

This, in fact, makes using pointers with STL containers hard, because the STL containers won't manage the lifetime of the pointed-to objects. You may want to look at Boost's pointer containers library. Otherwise, you'll either (1) want to use STL containers of smart pointers (like boost:shared_ptr which is legal for STL containers) or (2) manage the lifetime of the pointed-to objects some other way. You may already be doing (2).

幸福不弃 2024-09-20 18:42:21

我的回答并没有直接解决您最初的担忧,但您似乎遇到了这个问题,因为您有一个存储指针类型的 STL 容器。

Boost 提供了 ptr_container 库来解决这些问题情况类型。例如,ptr_vector 内部存储指向类型的指针,但通过其接口返回引用。请注意,这意味着容器拥有指向实例的指针并将管理其删除。

这是一个演示这个概念的简单示例。

#include <string>
#include <boost/ptr_container/ptr_vector.hpp>

void foo()
{
    boost::ptr_vector<std::string> strings;

    strings.push_back(new std::string("hello world!"));
    strings.push_back(new std::string());

    const std::string& helloWorld(strings[0]);
    std::string& empty(strings[1]);
}

My answer doesn't directly address your initial concern, but it appears you encounter this problem because you have an STL container that stores pointer types.

Boost provides the ptr_container library to address these types of situations. For instance, a ptr_vector internally stores pointers to types, but returns references through its interface. Note that this implies that the container owns the pointer to the instance and will manage its deletion.

Here is a quick example to demonstrate this notion.

#include <string>
#include <boost/ptr_container/ptr_vector.hpp>

void foo()
{
    boost::ptr_vector<std::string> strings;

    strings.push_back(new std::string("hello world!"));
    strings.push_back(new std::string());

    const std::string& helloWorld(strings[0]);
    std::string& empty(strings[1]);
}
少女七分熟 2024-09-20 18:42:21

如果您希望容器实际上包含动态分配的对象,则不应使用原始指针。使用 unique_ptr 或任何类似的合适类型。

If you want the container to actually contain objects that are dynamically allocated, you shouldn't be using raw pointers. Use unique_ptr or whatever similar type is appropriate.

往事风中埋 2024-09-20 18:42:21

这没有任何问题,但请注意,在机器代码级别,引用通常与指针相同。因此,当分配给引用时,通常指针并没有真正取消引用(没有内存访问)。
因此,在现实生活中,引用可能为 0,并且在使用引用时会发生崩溃 - 这可能会比其赋值晚得多。

当然,到底发生什么在很大程度上取决于编译器版本和硬件平台以及编译器选项和引用的确切用法。

正式地,取消引用 0 指针的行为是未定义,因此任何事情都可能发生。这个任何事情包括它可能立即崩溃,但也包括它可能稍后崩溃或永远不会崩溃。

因此,请务必确保永远不要将 0 指针分配给引用 - 像这样的错误很难找到。

编辑:将“通常”改为斜体,并添加有关官方“未定义”行为的段落。

There's nothing wrong with it, but please be aware that on machine-code level a reference is usually the same as a pointer. So, usually the pointer isn't really dereferenced (no memory access) when assigned to a reference.
So in real life the reference can be 0 and the crash occurs when using the reference - what can happen much later than its assignemt.

Of course what happens exactly heavily depends on compiler version and hardware platform as well as compiler options and the exact usage of the reference.

Officially the behaviour of dereferencing a 0-Pointer is undefined and thus anything can happen. This anything includes that it may crash immediately, but also that it may crash much later or never.

So always make sure that you never assign a 0-Pointer to a reference - bugs likes this are very hard to find.

Edit: Made the "usually" italic and added paragraph about official "undefined" behaviour.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文