通过引用传递C ++中的对象

时间:2013-07-28 05:23:57

标签: c++ compiler-errors parameter-passing

我有一些物品放在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);    

任何人都可以解释我在指针之间的混淆和通过引用传递吗?我是否必须进行某种演员表?

2 个答案:

答案 0 :(得分:0)

static PlantingHUD* createLayer(Object &o);

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

Object* obj1 = v.at(f);

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

这就是问题所在。

答案 1 :(得分:0)

void foo(Object o)

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

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

void foo(Object& o)

声明一个函数foo,它将通过对'Object'的现有实例的引用开始执行,该引用将被称为'o'。如果你捅或刺它,你将改变原来的。

这称为“按参考传递”。

void foo(Object* o)

声明一个函数foo,它将开始用一个名为“o”的变量执行,该变量包含应该是“Object”实例的地址。如果通过执行类似“o = nullptr”的操作来更改此变量,则只会影响foo内部的显示方式。但如果你把塞缪尔·L·杰克逊送到这个地址,他就可以提供持续超过foo生命的愤怒的工作。

void foo(Object*& o)

声明一个函数foo,它将开始用一个名为“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上

Live demo

按值传递

这个术语在人们的C ++开发早期就会让人感到困惑,因为在非专业术语中,听起来这就是“Object&amp; foo”会做的事情。

术语“按值传递”实际上源于语言必须做的事情来调用这样的函数,将整个原始对象/结构值有价值地复制到堆栈中,或者在复制文件的情况下可用,将它们转发给一个按值构造的构造函数,然后重新创建原始值的副本。

对于大多数简单情况,应该使用按值传递,在这种情况下,您不希望从您正在调用的函数中对当前作用域中的值产生副作用。

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

VS

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”会导致狗被处理掉。但请记住,因为我们传入了一个引用,它“babysitDog”它也从调用者那里消失了,如果那是使用引用...流浪者死了,戴夫,死了。

与指针一样,如果您要将引用存储在您可以访问它们的范围之外,那么您将负责确保引用的对象附着或者在引用之前从容器中删除引用。物体确实消失了。