инициализировать ссылку в списке инициализации - PullRequest
5 голосов
/ 09 февраля 2010

Мне сказали, что ссылочная переменная должна быть инициализирована в списке инициализации, но почему это неправильно?

   class Foo
    {
    public: 
        Foo():x(0) {      
         y = 1;
        }
    private:
        int& x;
        int y;
    };

Потому что 0 это временный объект? Если да, то на какой объект можно ссылаться? Объект, который может взять адрес?

Спасибо!

Ответы [ 3 ]

15 голосов
/ 09 февраля 2010

0 это не lvalue, это rvalue. Вы не можете изменить его, но вы пытаетесь привязать ссылку, где он может быть изменен.

Если вы сделаете ссылку const, она будет работать как положено. Учтите это:

int& x = 0;
x = 1; // wtf :(

Это, очевидно, запрет. Но const& могут быть связаны с временными значениями (rvalues):

const int& x = 0;
x = 1; // protected :) [won't compile]

Обратите внимание, что время жизни временного объекта заканчивается по завершении конструктора. Если вы сделаете static-хранилище для вашей константы, вы будете в безопасности:

class Foo
{
public:
    static const int Zero = 0;

    Foo() : x(Zero) // Zero has storage
    {
        y = 1;
    }
private:
    const int& x;
    int y;
};
0 голосов
/ 09 февраля 2010

Ну, вы никогда не можете изменить его, 0 никогда не может быть равно ничему, кроме 0.

try

 class Foo
    {
    public: 
        Foo(int& a):x(a) {      
         y = 1;
        }
    private:
        int& x;
        int y;
    };

В качестве альтернативы, вы можете сделать это, если ваша ссылка постоянна, потому что тогда 0может только равняться нулю

0 голосов
/ 09 февраля 2010

Долгоживущая ссылка должна быть привязана к lvalue. По сути, как вы выразились красноречиво, это объект с определенным адресом. Если они связаны с временным, временное будет уничтожено, пока ссылка все еще ссылается на него, и результаты не определены.

Кратковременные константные ссылки (локальные переменные функций и аргументы функций) могут быть связаны с временными. Если это так, то временный объект гарантированно не будет уничтожен, пока ссылка не выйдет за рамки.

Демонстрационный код:

#include <iostream>

class Big {
 public:
   Big() : living_(true), i_(5) { // This initialization of i is strictly legal but
      void *me = this;            // the result is undefined.
      ::std::cerr << "Big constructor called for " << me << "\n";
   }
   ~Big() {
      void *me = this;
      living_ = false;
      ::std::cerr << "Big destructor called for " << me << "\n";
   }

   bool isLiving() const { return living_; }
   const int &getIref() const;
   const int *getIptr() const;

 private:
   ::std::string s_;
   bool living_;
   const int &i_;
   char stuff[50];
};

const int &Big::getIref() const
{
   return i_;
}

const int *Big::getIptr() const
{
   return &i_;
}

inline ::std::ostream &operator <<(::std::ostream &os, const Big &b)
{
   const void *thisb = &b;
   return os << "A " << (b.isLiving() ? "living" : "dead (you're lucky this didn't segfault or worse)")
             << " Big at " << thisb
             << " && b.getIref() == " << b.getIref()
             << " && *b.getIptr() == " << *b.getIptr();
}

class A {
 public:
   A() : big_(Big()) {}

   const Big &getBig() const { return big_; }

 private:
   const Big &big_;
};

int main(int argc, char *argv[])
{
   A a;
   const Big &b = Big();
   const int &i = 0;
   ::std::cerr << "a.getBig() == " << a.getBig() << "\n";
   ::std::cerr << "b == " << b << "\n";
   ::std::cerr << "i == " << i << "\n";
   return 0;
}

А на выходе:

Big constructor called for 0x7fffebaae420
Big destructor called for 0x7fffebaae420
Big constructor called for 0x7fffebaae4a0
a.getBig() == A living Big at 0x7fffebaae420 && b.getIref() == -341121936 && *b.getIptr() == -341121936
b == A living Big at 0x7fffebaae4a0 && b.getIref() == 0 && *b.getIptr() == 0
i == 0
Big destructor called for 0x7fffebaae4a0
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...