26

私がこれを行うとき(私のクラスで)

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_eyessf::Spriteオブジェクトおよび/または参照であると言います。

これが機能しないのはなぜですか?宣言は同じではありませんか?

4

5 に答える 5

63

sf::Sprite* re_sprite_hair, re_sprite_body, re_sprite_eyes;

3 つのポインターを宣言しません。1 つのポインターと 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;

変数ごとに星を 1 つ付ける必要があります。そのような場合、この状況を正確に明確にするために、型ではなく変数の側にスターを付けておくことを好みます。

于 2012-11-29T03:20:36.533 に答える
26

C と C++ の両方で、は型指定子ではなく宣言*子にバインドします。どちらの言語でも、宣言はオブジェクトではなく式の型に基づいています。

たとえば、int名前付きへのポインターがあり、その値pにアクセスしたいとします 。次のように、単項演算子でポインターを逆参照することでこれを行います。intp*

x = *p;

のタイプ*pint; したがって、の宣言p

int *p;

これは、同じ宣言ステートメント内で宣言するポインターの数に関係なく当てはまります。qおよびrもポインターとして宣言する必要がある場合は、宣言子の一部として単項も必要です*

int *p, *q, *r;

*q*r型があるためintです。T *p、、T* pまたはT * p; と書くことができるのは、C および C++ 構文の偶然です。これらの宣言はすべて として解釈されT (*p)ます。

これが、ポインターと参照型を次のように宣言する C++ スタイルが好きではない理由です。

T* p;
T& r;

これは、C および C++ の宣言構文がどのように機能するかについて誤った見方をしていることを意味し、まさにあなたが経験したよう混乱を招くからです。しかし、私は C++ を十分に書いてきたので、そのスタイルによってコードの意図が明確になる場合があることを理解できました。特にコンテナー タイプを定義する場合はそうです

しかし、それはまだ間違っています。


T* pこれは (2 年遅れて) Lightness Races in Orbit への応答です (そして、私がこの規則を「間違っている」とラベル付けすることに反対する人は誰でも)...

まず第一に、これとまったく同じように、規則の使用と、それが人々の期待どおりに機能しないことから具体的に生じる多くの質問があります。このサイトには、「 と の両方をポインターとして宣言T* pしないのはなぜですか?」という質問がいくつありますか?T* p, qpq

それ混乱をもたらします - それ自体でその使用を思いとどまらせるのに十分なはずです.

しかし、それ以上は一貫性がありません。宣言子から配列性または関数性を分離することはできません。なぜポインター性を分離する必要があるのでしょうか。

「それは、[]and()が後置演算子であり、 while*が単項だからです」. はい、そうです。では、演算子をオペランドに関連付けないのはなぜですか? 宣言T* pでは、Tは のオペランドではない*のに、なぜそうであるかのように宣言を書くのでしょうか?

aが「ポインタの配列」である場合、なぜ と書く必要があるのでしょうT* a[N]か? f「ポインタを返す関数」の場合、なぜ と書く必要があるのでしょうT* f()か? これらの宣言をおよびとして記述した場合、宣言子システムはより理にかなっていて、内部的に一貫性があります。これは、任意の型 (実際、宣言指定子の任意のシーケンス) の代用として使用できるという事実から明らかなはずです。T *a[N]T *f()T

そして、配列へのポインターと関数へのポインターがあり、宣言子1*に明示的にバインドする必要があります。

T (*a)[N];
T (*f)();

はい、ポインター性は宣言するものの重要なプロパティですが、配列性と機能性も同様であり、一方を他方よりも強調すると、解決するよりも多くの問題が発生します。繰り返しますが、この質問が示すように、T* p慣習は混乱を招きます。

*は単項であり、それ自体が別のトークンであるため、 、 、 、および と書くことができます。T* pこれらT *pT*pすべてT * pコンパイラによって受け入れられますが、すべて として解釈されT (*p)ます。さらに重要なことは、T* p, q, rとして解釈されT (*p), q, rます。と書くと、その解釈はより明白になりますT *p, q, r。ええ、ええ、ええ、「1 行に 1 つだけ宣言すれば問題ありません」。他に問題にしない方法を知っていますか? 宣言子を適切に記述してください。宣言子システム自体がより理にかなっており、間違いを犯す可能性が低くなります。

私たちは言語の「アンティークの奇妙さ」について議論しているわけではありません。それは言語の文法とその哲学の基本的な構成要素です。ポインター性は、配列性や関数性と同様に宣言子のプロパティであり、混乱を招くだけでなく、C と C++ の両方を必要以上に理解するのが難しくなります。

逆参照演算子を後置ではなく単項にするのは間違いだと私は主張します2が、それが B でどのように機能したかであり、Ritchie はできるだけ多くの B を保持したいと考えました。T* pまた、Bjarne による大会の推進は誤りであると主張します。


  1. 議論のこの時点で、誰かが次のような typedef の使用を提案するでしょう。
    typedef T arrtype[N]; 
    arrtype* p;
    これは要点を完全に見逃しており、「C: The Complete Reference」の初版は大きくて重く、他の何の役にも立たないため、提案者に打撃を与えています。
  2. T a*[N]*()とは対照的に、書くことT (*(*a)[N])()は間違いなく目が疲れにくく、スキャンがはるかに簡単です。
于 2012-11-29T03:59:11.780 に答える
5

C++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;};
于 2012-11-29T04:52:36.543 に答える
3

注意を喚起するもう 1 つの点は、次の行です。

int * p1, * p2;

これは、前の例で使用された 2 つのポインターを宣言します。ただし、両方のポインターに型 (int へのポインター)*を持たせるために、各ポインターにアスタリスク ( ) があることに注意してください。int*これは、優先順位規則により必要です。代わりに、コードが次の場合に注意してください。

int * p1, p2;

p1確かにタイプint*になりますが、p2タイプになりintます。この目的では、スペースはまったく問題になりません。とにかく、ステートメントごとに複数のポインターを宣言することに関心のあるほとんどのポインター ユーザーにとっては、ポインターごとに 1 つのアスタリスクを配置することを覚えておくだけで十分です。またはさらに良い: 変数ごとに異なるステートメントを使用します。

http://www.cplusplus.com/doc/tutorial/pointers/から

于 2015-03-25T05:49:50.057 に答える
1

アスタリスクはポインター変数名にバインドされます。これを覚える方法は、C/C++ では宣言が使用方法を模倣していることに注意することです。

ポインターは次のように使用できます。

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

同様に、

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

どちらの場合も、基になる型指定子と、式でその型のオブジェクトに「到達」するために必要な演算子 (複数可) があります。

于 2012-11-29T04:14:20.800 に答える