C++中的指针是什么?

4

在这个程序中,我有些困惑指针的使用:

#include<iostream.h>
class ABC
{
    public:
        int data;
        void getdata()
        {
            cout<<"Enter data: ";
            cin>>data;
            cout<<"The number entered is: "<<data;
        }
};
void main()
{
    ABC obj;
    int *p;
    obj.data = 4;
    p = &obj.data;
    cout<<*p;
    ABC *q;
    q = &obj.data;
    q->getdata();
}

我理解到的是直到以下步骤我明白: ABC *q;
它是什么意思?我的书上说这是一个类类型指针(语法可怜且含糊),但这是什么意思?一个指向 ABC 类地址的指针吗?
如果是这样的话,那么接下来的步骤让我感到困惑。q = &obj.data;
我们正在将此指针指向 data 的位置,该位置是一个变量。然后,ABC *q;如何适合其中呢?
最后一步又是什么呢?q->getdata();做了什么?我的书说这是“指向成员函数操作符”,但没有解释。
非常感谢您的帮助!

30
扔掉那本书。 - unwind
2
我假设 q = &obj.data 等同于 q = &obj - this
4
我很好奇——那本书的标题是什么?还是你已经扔掉了?:D - thokra
@unwind,我真的认为我会这样做。在学习Java时,我有一本非常好的书。这本书是由我的委员会指定的(还有其他几本)。我一定会再买一本。 - mikhailcazi
@thokra 一本名叫“R. D. Supekar”的家伙写的垃圾书:* XIIth Bifocal计算机科学*。 - mikhailcazi
显示剩余5条评论
4个回答

5
那本书是错误的,因为它应该是这样的:
ABC *q;
q = &obj;
q->getdata();

或者使用 int 指针:
ABC *q;
int *qq;
qq = &obj.data;
q = &obj;
q->getdata();

实际上,因为int数据是类&obj的第一个成员,因此&obj和&obj.data表示相同的地址。因此它是完全有效的。 - Pandrei
1
不,这并不是完全有效的。编译器会抱怨它无法将int转换为ABC。是的,它们将是相同的地址,但它们不是相同的类型,因此原始代码将无法编译。 - Speed8ump
@Pandrei 是的,这不仅仅关乎内存位置,数据类型也很重要。 - mikhailcazi

3
ABC * q

这个指令创建了一个指向内存片段的指针,该内存片段中包含类 ABC 的实例。例如:

q = new ABC();

这将实例化ABC并将该实例的地址存储在q变量中。

ABC abc;
q = &abc;

这个实例化 ABC 自动(也就是说,编译器负责分配和释放该实例的内存),并将该类的地址存储在 q 中。

此外,-> 不是指向成员函数的运算符。这只是写另一种方式的缩写:

a -> b

equals

(*a).b

如果你知道 a 指向一个类实例(或结构体实例,在 C++ 中是几乎相同的),并且想要访问该实例的成员(字段或方法),可以快速地编写 a->b = 5;a->DoSth(); 而不是 (*a).b = 5;(*a).DoSth();

2
有趣的事实 -> 并不是唯一一个像那样具有简单定义的C++运算符。数组下标运算符a[b]的定义为 *((a) + (b)),这意味着b[a]同样有效。 - Mgetz
这个指令创建一个指向内存片段的指针,该内存片段中驻留着类ABC的实例。我认为这是错误的。ABC *p;只声明了一个指针,如果您不将任何东西分配给该指针,则它没有意义;请想象编译器为声明生成了什么(假设它没有剥离未使用的符号)。 - Pandrei
这非常流畅。即使没有实际实例,它仍将是指向“ABC”实例的指针。在C++中,例如,可以分配一堆内存,将其转换为“ABC *”,有时甚至会起作用(如果“ABC”不是多态的,并且如果您分配了足够的内存),但是当然这是您绝对不想做的事情。关键是,编译器始终相信,在p所指向的位置上有“ABC”。 - Spook
@Pandrei 对不起,我完全不明白! :P - mikhailcazi
@Mgetz,“*((a) + (b))”中的括号究竟表示什么? - mikhailcazi
显示剩余2条评论

2
ABC obj;
ABC *q;
q = &obj.data;

不应该编译。

更合理的做法是

q = &obj;

这段代码将obj(一个ABC类的实例)的地址赋给了指向ABC的指针q

完成后,如果您想在q上调用getdata函数,可以使用以下方式:

q -> getdata();

因为 q 是一个指针,所以需要进行比较。与堆栈变量 obj 的处理方式相比,有所不同。

obj.getdata();

同一功能,有不同的调用方式。


实际上它可以编译并且非常好。问题出在以下语句:q = &(obj.data),因为您现在正在将一个 int* 分配给 ABC*,而编译器无法进行隐式转换。 - Pandrei
@Pandrei 不,它不能编译。我试过了。编译器突出显示了以下行:q=&obj.data; 并给出了以下错误:“无法将'int *'转换为'ABC *'”。 - mikhailcazi
加括号不会有任何区别。即使没有括号,编译器也会将obj.data作为一个元素接受。只是因为您没有在括号中包含整个内容,它不会将q指向简单的obj - mikhailcazi

2

'Lame-up-duck'所说的q = &obj.data部分是不正确的:

你的指针:

ABC *q;

指针是一个通常为32位或64位的变量。它可以包含对象的内存地址,此处的对象类型为ABC

因此,在obj中有这个对象。

要让指针实际“指向”该对象,您需要使用“地址运算符”&将内存地址值分配给指针,如下所示:

q = &obj;

q现在指向obj对象。您可以使用以下方式访问此obj对象:

q->

现在,q->getdata()将调用obj中的getdata函数。

q现在指向obj.data对象。”你确定吗? :-) - Grimm The Opiner
1
@user1158692:谢谢:),我因为这本“糟糕的书”而感到困惑。 - Stefan

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接