-1

我有放入std::vector. 稍后我需要遍历向量并更改每个位置的对象中的一些成员变量。

我想我想将它传递by reference给一个函数来操作它,但我似乎遇到了一个错误:

Non-const lvalue reference to type 'Object' cannot bind to a value of unrelated type 'Object *'

以下是省略代码的一般要点:

Object* o1 = Object::createWithLocation(p.x, p.y);
v.push_back(o1); 

// later on
for (int f=0; f < v.size(); f++)
{
    Object* obj1 = v.at(f);

    addChild(h->createLayer(obj1), 3); // add the HUD
}

createLayer定义为:

static PlantingHUD* createLayer(Object &o);    

谁能解释我在指针和引用传递之间的混淆?我必须做某种演员吗?

4

2 回答 2

0
void foo(Object o)

声明一个函数 foo,它将从名为 'o' 的类 'Object' 的新实例开始执行。

这称为“按值传递”,但更准确地说是“复制”,因为 foo 收到的是它自己的、我们称之为 foo 的 Object 实例的个人副本。当“foo”结束时,它所知道、被喂养和完成学业的“Object o”将不复存在。

void foo(Object& o)

声明一个函数 foo,它将以对“对象”的现有实例的引用开始执行,该引用将被称为“o”。如果你戳或戳它,你将改变原来的。

这称为“按引用传递”。

void foo(Object* o)

声明一个函数 foo,它将从一个名为“o”的变量开始执行,该变量包含应该是“Object”实例的地址。如果您更改此变量,通过执行“o = nullptr”之类的操作,它只会影响 foo 内部的外观。但是,如果您将塞缪尔·杰克逊 (Samuel L Jackson) 发送到该地址,他可以进行持续超过 foo 生命周期的愤怒报复。

void foo(Object*& o)

声明一个函数 foo,它将从一个名为“o”的变量开始执行,该变量是对指向对象 o 实例的指针的引用——它就像一个别名,除了没有编译器优化,它实际上是由编译器使用一种指针。

让我们分别尝试这些。

#include <iostream>
#include <cstdint>

struct Object
{
    int m_i;

    void event(const char* what, const char* where)
    {
        std::cout <<
            what<< " " << (void*)this <<
            " value " << m_i <<
            " via " << where <<
            std::endl;
    }

    // Construct an object with a specific value.
    Object(int i) : m_i(i)
    {
        event("Constructed", "Operator(int i)");
    }

    // This is called the copy constructor, create one object from another.
    Object(const Object& rhs) : m_i(rhs.m_i)
    {
        event("Constructed", "Operator(const Object&)");
    }

    // This is how to handle Object o1, o2; o1 = o2;
    Object& operator=(const Object& rhs)
    {
        m_i = rhs.m_i;
        event("Assigned", "operator=");
        return *this;
    }

    // Handle destruction of an instance.
    ~Object() { event("Destructed", "~Object"); }
};

void foo1(Object o)
{
    std::cout << "Entered foo1, my o has value " << o.m_i << std::endl;
    // poke our local o
    o.m_i += 42;
    std::cout << "I changed o.m_i, it is " << o.m_i << std::endl;
}

void foo2(Object* o)
{
    std::cout << "Foo2 starts with a pointer, it's value is " << (uintptr_t)o << std::endl;
    std::cout << "That's an address: " << (void*)o << std::endl;
    std::cout << "m_i of o has the value " << o->m_i << std::endl;
    o->m_i += 42;
    std::cout << "I've changed it tho, now it's " << o->m_i << std::endl;
}

void foo3(Object& o)
{
    std::cout << "foo3 begins with a reference called o, " << std::endl <<
        "which is sort of like a pointer but the compiler does some magic " << std::endl <<
        "and we can use it like a local concrete object. " <<
        std::endl <<
        "Right now o.m_i is " << o.m_i <<
        std::endl;
    o.m_i += 42;
    std::cout << "Only now, it is " << o.m_i << std::endl;
}

void foo4(Object*& o)
{
    std::cout << "foo4 begins with a reference to a pointer, " << std::endl <<
        "the pointer has the value " << (uintptr_t)o << " which is " <<
        (void*)o <<
        std::endl <<
        "But the pointer points to an Object with m_i of " << o->m_i << std::endl <<
        "which we accessed with '->' because the reference is to a pointer, " <<
        "not to an Object." <<
        std::endl;
    o->m_i += 42;
    std::cout << "I poked o's m_i and now it is " << o->m_i << std::endl;
    // Now for something really dastardly.
    o = new Object(999);
    std::cout << "I just changed the local o to point to a new object, " <<
        (uintptr_t)o << " or " << (void*)o << " with m_i " << o->m_i <<
        std::endl;
}

int main()
{
    std::cout << "Creating our first objects." << std::endl;
    Object o1(100), o2(200);

    std::cout << "Calling foo1 with o1" << std::endl;
    foo1(o1);
    std::cout << "back in main, o1.m_i is " << o1.m_i << std::endl;

    std::cout << "Calling foo2 with &o1" << std::endl;
    foo2(&o1);
    std::cout << "back in main, o1.m_i is " << o1.m_i << std::endl;

    std::cout << "Calling foo3(o2), which looks like the way we called foo1." << std::endl;
    foo3(o2);
    std::cout << "back in main, o2.m_i is " << o2.m_i << std::endl;

    std::cout << "Creating our pointer." << std::endl;
    Object* optr;
    std::cout << "Setting it to point to 'o2'" << std::endl;
    optr = &o2;
    std::cout << "optr now has the value " << (uintptr_t)optr <<
        " which is the address " << (void*)optr <<
        " which points to an Object with m_i = " << optr->m_i <<
       std::endl;

    foo4(optr);

    std::cout << "back in main, o2 has the value " << o2.m_i << std::endl <<
        "and now optr has the value " << (uintptr_t)optr << std::endl <<
        "and optr->m_i is now " << optr->m_i <<
        std::endl;

    if (optr != &o2)
        delete optr; // otherwise we'd technically be leaking memory.

    return 0;
}

ideone.com 上的现场演示

按值传递

这个术语在 C++ 开发的早期使人们感到困惑,因为用通俗的话来说,这听起来像是“Object& foo”会做的事情。

术语“按值传递”实际上源于语言必须执行的调用此类函数的操作,以按值方式将整个原始对象/结构复制到堆栈上,或者在复制 ctor 可用的情况下,将它们转发给按值构造的构造函数并重新创建原始值的副本。

值传递应该用于最简单的情况,在这些情况下,您不希望正在调用的函数对当前范围内的值产生副作用。

bool checkWidthdrawl(Dollars balance, Dollars amountToWithdraw)
{
    // it's safe for me to change "balance" here because balance is mine
}

对比

bool checkWidthdrawl(Dollars& balance, Dollars amountToWithdraw)
{
    balance -= amountToWithdraw;
    if (balance < 0)
        std::complaint << "My account seems to be missing $" << amountToWithdraw;
}

但是,通过引用传递可能会变得昂贵。

struct FourK { char a[1024], b[1024], c[1024], d[1024]; }

如果你整天按值传递它,你就有可能在某个时候炸毁你的堆栈,以及花费大量时间复制所有这些字节。

void foo(int i); // Unless you need to see the changes to i, this is perfectly fine.
void foo(FourK f); // Someone should hunt you down and yell "PEANUT" in your ear.

通过引用传递

引用实际上是指针系统的契约,它允许语言确保您真正谈论的是对象的具体实例,从而允许您引用函数外部值的预先存在的实例。

当然,有办法打破这一点,但语言非常非常努力地让它们难以做到。例如,尝试将其添加到上面的代码中:

Object& makeObjectNotWar(int i)
{
    Object thisObjectGoesAway(i);
    return thisObjectGoesAway /*right about now*/;
}

您还可以向调用者保证该函数不会对带有“const”修饰符的变量产生任何副作用。

void fooc(const Object& o)
{
    o.m_i += 42; // Error
}

您甚至可以在函数中使用它作为对您自己(和编译器)的提示,您不想意外更改值,这是一个可以为编译器提供优化提示的情况:

std::vector<int> foo;
add1000valuesTo(foo);
const size_t fooSize = foo.size();
for (size_t i = 0; i < fooSize; ++i) {
    // ... stuff you're sure won't decrease foo.size()
}

没有 const fooSize

for (size_t i = 0; i < foo.size(); ++i) {

编译器必须首先假设“foo.size()”可以在循环的任何给定迭代中更改。它可能会发现它没有,但是通过给它提示,你节省了一点编译时间,可能提高了你的性能,并使人们更容易准确地判断出你所期望的行为。缺点:如果你的循环确实改变了 foo 的大小,你会通过错误报告发现:(

关于传递引用的最后一件事是 C++ 引用不受保护或“引用计数”。该语言只承诺引用将在其范围内有效,只要您不做任何愚蠢的事情,比如调用删除对象的东西。

// Author intended this function to be called
// by the owner of a Dog.
void doneWithFoo(Dog& dog)
{
    Dog* deadDog = &dog;
    delete deadDog;
}

Rover& Babysitter::babysitDog(Dog& rover, int hours)
{
    rover.feed(FeedType::Donut);
    if (rover.pooped())
       doneWithDog(rover);
    // ...
    return rover; // I have a bad feeling about this.
}

显然,您不会期望“babysitDog”会导致狗被丢弃。但是请记住,因为我们传入了一个引用,所以它也从调用者那里消失了,如果那是使用引用... rover 死了,Dave,死了。

与指针一样,如果您要将引用存储在您可以访问它们的范围之外,那么您将负责确保被引用的对象保持不变,或者在对象离开之前从容器中删除引用离开。

于 2013-07-28T07:56:31.897 回答
0
static PlantingHUD* createLayer(Object &o);

此方法需要对 Object 的引用作为参数,但您的输入是一个指针。

Object* obj1 = v.at(f);

addChild(h->createLayer(obj1), 3); // add the HUD

那就是问题所在。

于 2013-07-28T06:31:24.720 回答