Объявление нескольких указателей объектов в одной строке вызывает ошибку компилятора

когда я это делаю (в своем классе)

public:
    Entity()
    {
        re_sprite_eyes = new sf::Sprite();
        re_sprite_hair = new sf::Sprite();
        re_sprite_body = new sf::Sprite();
    }

private:
    sf::Sprite* re_sprite_hair;
    sf::Sprite* re_sprite_body;
    sf::Sprite* re_sprite_eyes;

Все работает отлично. Однако, если я изменю объявления на это:

private:
    sf::Sprite* re_sprite_hair, re_sprite_body, re_sprite_eyes;

Я получаю эту ошибку компилятора:

error: no match for 'operator=' in '((Entity*)this)->Entity::re_sprite_eyes = (operator new(272u), (<statement>, ((sf::Sprite*)<anonymous>)))

И затем он говорит, что кандидаты на re_sprite_eyes являются sf::Sprite объектами и/или ссылками.

Почему это не работает? Разве объявления не совпадают?

Ответ 1

sf::Sprite* re_sprite_hair, re_sprite_body, re_sprite_eyes;

Не объявляет 3 указателя - это один указатель и 2 объекта.

sf::Sprite*, к сожалению, не относится ко всем объявленным после него переменным, только первым. Это эквивалентно

sf::Sprite* re_sprite_hair;
sf::Sprite re_sprite_body;
sf::Sprite re_sprite_eyes;

Вы хотите сделать:

sf::Sprite *re_sprite_hair, *re_sprite_body, *re_sprite_eyes;

Вам нужно поместить одну звезду для каждой переменной. В таких случаях я предпочитаю держать звезду на переменной стороне, а не на типе, чтобы сделать эту ситуацию ясной.

Ответ 2

В C и С++ * привязывается к объявлению, а не к спецификатору типа. В обоих языках декларации основаны на типах выражений, а не на объектах.

Например, предположим, что у вас есть указатель на int с именем p, и вы хотите получить доступ к значению int, на которое указывает p; вы делаете это путем разыменования указателя унарным оператором *, например:

x = *p;

Тип выражения *p равен int; Таким образом, декларация p есть

int *p;

Это верно, независимо от того, сколько указателей вы заявляете в одном заявлении декларации; если q и r также должны быть объявлены как указатели, то они также должны иметь унарный * как часть декларатора:

int *p, *q, *r;

потому что выражения *q и *r имеют тип int. Это случайность синтаксиса C и С++, которую вы можете написать T *p, T* p или T * p; все эти декларации будут интерпретироваться как T (*p).

Вот почему я не люблю стиль С++ для объявления указателей и ссылочных типов как

T* p;
T& r;

потому что это подразумевает неправильное представление о синтаксисе объявления C и С++, что приводит к точному виду замешательства, которое вы только что испытали. Однако я написал достаточно С++, чтобы понять, что бывают случаи, когда этот стиль делает код более понятным, особенно при определении типов контейнеров.

Но это все еще неправильно.

Ответ 3

В С++ 11 у вас есть небольшое небольшое обходное решение, которое может быть лучше, чем перемещение пробелов вперед и назад:

template<typename T> using type=T;
template<typename T> using func=T*;

// I don't like this style, but type<int*> i, j; works ok
type<int*> i = new int{3},
           j = new int{4};

// But this one, imho, is much more readable than int(*f)(int, int) = ...
func<int(int, int)> f = [](int x, int y){return x + y;},
                    g = [](int x, int y){return x - y;};

Ответ 4

Еще одна вещь, которая может привлечь ваше внимание, - это строка:

int * p1, * p2;

Это объявляет два указателя, используемые в предыдущем примере. Но обратите внимание, что для каждого указателя есть звездочка (*), чтобы у обоих был тип int* (указатель на int). Это необходимо из-за правил приоритета. Обратите внимание, что если вместо этого код был:

int * p1, p2;

p1 действительно будет иметь тип int*, но p2 будет иметь тип int. Пространства для этой цели не имеют значения. Но в любом случае просто помнить о том, чтобы поставить одну звездочку на указатель, достаточно для большинства пользователей-указателей, заинтересованных в объявлении нескольких указателей на оператор. Или еще лучше: используйте разные statemet для каждой переменной.

Из http://www.cplusplus.com/doc/tutorial/pointers/

Ответ 5

Звездочка привязывается к имени переменной-указателя. Способ запомнить это - заметить, что в C/С++ объявления мимируют использование.

Указатели могут использоваться следующим образом:

sf::Sprite *re_sprite_body;
// ...
sf::Sprite sprite_bod = *re_sprite_body;

Аналогично,

char *foo[3];
// ...
char fooch = *foo[1];

В обоих случаях существует базовый тип-спецификатор, а оператор или операторы должны "получить" объект этого типа в выражении.