3

我知道指向 (->) 和点 (.) 运算符之间的区别,但我不明白为什么需要两个棱角?不使用指针而只使用点运算符不是很容易吗?来自http://www.programcreek.com/2011/01/an-example-of-c-dot-and-arrow-usage/

    #include <iostream>

    using namespace std;

    class Car
    {
    public:
int number;

void Create() 
{
    cout << "Car created, number is:  " << number << "\n" ;
}
    };

    int main() {
Car x;
// declares x to be a Car object value,
// initialized using the default constructor
// this very different with Java syntax Car x = new Car();
x.number = 123;
x.Create();

Car *y; // declare y as a pointer which points to a Car object
y = &x; // assign x's address to the pointer y
(*y).Create(); // *y is object
y->Create();

y->number = 456; // this is equal to (*y).number = 456;
y->Create();
    }

为什么要费心使用指针?只需像 X 一样创建 Y,它的工作原理是一样的。如果您说您需要动态分配内存的指针,那么为什么还要使用点运算符呢?

4

7 回答 7

6

我认为你混合了两个不同的问题。

首先,->运营商是不必要的,是的。x->y等价于(*x).y,但->操作符更容易输入,所以基本上只是为了方便。

第二部分是是否使用指针。你是对的,通常你不应该这样做。默认情况下,只需在那时和那里创建您的对象,并直接引用它们:

Foo bar;
bar.baz():

但是在很多情况下仍然需要指针。对象需要能够引用其他对象。引用可以做到这一点,但不能重新安装。一旦初始化,它将始终指向同一个对象。

可以更新指针以指向不同的对象。

链表、树数据结构和无数其他东西都依赖于能够指向其他对象的对象。

所以是的,我们需要指针。但我们不需要操作员->。我们只是使用它,因为它很方便。

于 2012-04-08T10:33:00.397 回答
2

一种。它只是更容易在语义上理解代码,而无需查看类型或使用特殊符号(如m_pszMyName. 您可以立即告诉阅读代码什么是指针,什么是值。

湾。想想 shared_ptr 和覆盖运算符的情况。shared_ptr<T>->get()表示除 之外的东西shared_ptr<T>.get()。第一个是指向对象中的函数,第二个是 shared_ptr 类本身的函数。这只是一个例子,但你明白这一点。

于 2012-04-08T10:24:43.687 回答
1

从您的链接:

下面的例子应该是一个很好的例子。

这实际上有点令人困惑。为什么你会在堆栈 ( Car x;) 上创建一个对象,然后创建一个指向它的指针以使用它访问它->

而不是试图回答隐含的问题“我们为什么需要指针?” 我将尝试消除该示例可能引起的任何混淆。

在您的评论中,您说:

我想知道创建不同的对象之间是否存在差异。

在这个例子中,只有一个对象,Car堆栈​​上由创建Car x;(完整地说,堆栈上还有一个Car指针,由创建Car *y;)。它们在退出时超出范围main(),因此它们的内存被清理。

但是还有另一种创建对象的方法,我猜你已经根据你的评论知道了,这是用来在堆上new初始化它们的:。堆上的对象永远不会超出范围,因此您可以在调用的函数退出后继续使用它们,但您必须使用显式清理它们以防止内存泄漏。Car *z = new Car;newdelete

就这样,一个指向对象的指针更现实的使用:返回值new

于 2012-04-08T11:05:34.600 回答
0

不使用指针而只使用点运算符不是很容易吗?

C/C++ 与其他高阶语言一样,不会用一些糖衣语法封装指针。指针是自然产生的,下面的列表并不详尽

  1. 从堆中分配内存。静态数据分配,或在堆栈中分配存储总是不可行的。所有权转移、空间限制和程序的动态性质都会产生开销。
  2. 读取和写入文件。
  3. 迭代对象,包括 C-Type 字符串。您可以使用数组访问语法,但安全性差异很小,并且当您传递给函数时数组会退化为指针(大小信息丢失)。

从 C++ 的角度来看,以上所有内容都可以封装到对象中。

  1. 通过 iotream 文件 IO
  2. 通过智能指针的指针(一些来自 C++98,一些来自 C++11 或 eve boost)
  3. STL 类型对象的迭代器。
  4. 使用参考

尽管如此,即使在您没有明确看到它们的语言中,指针也存在。它们只是被封装到更高阶的对象中。

这在一定程度上解释了为什么我们不能超越指针思考,你可能感兴趣的下一部分是语法。为什么我们完全需要ptr->somemember而不是(*ptr).somemember. 它只是重复使用的简写。C/C++ 程序员已经习惯了它,到目前为止,我还没有看到使用多余语法的单个程序。

于 2012-04-08T10:29:20.777 回答
0

-> 只是简写。考虑一个代表树节点的类:

struct node {
    int data;
    node* left;
    node* right;
};

成员left是指向节点左孩子的指针。假设我们有一个指向某个节点的指针p,现在我们要获取指向 p 的左孩子的左孩子的右孩子的指针,使用 dot 我们必须写(*(*(*p).left).left).right,难以阅读且容易出错,使用 ->我们可以简单地写p->left->left->right,很清楚。

于 2012-04-08T11:09:44.393 回答
0

C++中同时存在->and.运算符是受 C 语言的直接影响。C 区分了通过指针访问对象和访问在当前作用域中声明的对象。

在 C++ 中,引用是访问本地范围对象的自然扩展。

不知道 C 的创造者有没有考虑到这一点,但我一直把它当作一个小的优化指南。查看一段代码,您可以看到->它将在运行时计算最终地址,而.运算符将在编译时计算地址。这甚至在访问结构成员时也有效。考虑以下几点:myptr->mMember.mValue从 mMember 到 mValue 的偏移量可以在编译时计算,而指针的最终地址计算必须在运行时计算。我承认,就目前的优化而言,这是一个次要的考虑,并且在 C++ 中的引用不再可能这样做,但在 20 年前,这是需要牢记的。

于 2012-04-08T11:22:53.267 回答
0

是的,你总是可以使用(*x).member而不是x->member,但是你真的想要 whenx是一个复杂的表达式吗?

将相关的东西(*.这种情况下)放在很远的地方会使源代码的可读性降低,因此->“在一个地方”只是一种更好的语法。


至于需要指针的概念,主要有2个原因:

1. 对象生命周期

有两种分配对象的方法

  • 在堆栈上。
  • 在动态内存中。

堆栈随着执行流程进入和退出函数而缠绕和展开,因此堆栈对象的生命周期不可避免地与我们在创建它的函数中停留的时间有关。

如果您需要一个比创建它的函数寿命更长的对象,则需要在动态内存中创建它,识别此类对象的唯一方法是通过其内存地址,也就是。指针。

2.对象共享

是否有多个其他对象需要访问该对象?如果是,那么这些其他对象没有办法引用共享对象,除了持有它的地址。

即使您只有一个其他对象,但它们的生命周期不同,“生命周期”的原因也适用。如果只有一个其他对象并且它们的生命周期匹配,则将其设为字段。

于 2012-04-08T11:59:54.320 回答