C ++:使用指针的场景是什么?“好主意”(TM)?

时间:2010-10-27 04:29:14

标签: c++ pointers

  

可能重复:
  Common Uses For Pointers?

我仍在学习C ++的基础知识,但我已经足够了解有用的小程序。

我理解指针的概念以及我在教程中看到的示例对我有意义。但是,在实际层面上,作为(以前的)PHP开发人员,我还不能确信在我的程序中实际使用它们。

事实上,到目前为止,我还没有感到需要来使用任何指针。我有我的类和函数,我似乎完全没有使用任何指针(更不用指针指针)。我不禁为我的小程序感到有些自豪。

但是,我知道我错过了 C ++最重要的功能之一,一个双刃:指针和内存管理可能造成破坏,看似随机崩溃,很难找到bug和安全性孔...但同时,使用得当,它们必须允许巧妙而有效的编程

所以:不要使用指针告诉我我错过了什么。

什么是好的场景哪里使用指针是必须的?
他们允许你做什么,否则你不能这样做? 它们以哪种方式使您的程序更有效率?

指针指针怎么样?

[编辑:所有各种答案都很有用。 SO的一个问题是我们不能“接受”不止一个答案。我经常希望我能。实际上,所有答案的组合有助于更好地理解整体情况。感谢。]

11 个答案:

答案 0 :(得分:9)

当我想让一个类访问一个对象时,我使用指针,而不给它该对象的所有权。即使这样,我也可以使用引用,除非我需要能够更改我正在访问的对象和/或我需要没有对象的选项,在这种情况下指针将为NULL。< / p>

答案 1 :(得分:7)

This question has been asked on SO before。我的回答是:

我在我编写的C ++代码中每六行使用一次指针。在我的脑海中,这些是最常见的用途:

  • 当我需要动态创建一个寿命超过创建范围的对象时。
  • 当我需要在编译时分配一个大小未知的对象时。
  • 当我需要将一个对象的所有权从一个东西转移到另一个东西而不实际复制它时(比如链接列表/堆/任何非常大的,昂贵的结构)
  • 当我需要从两个不同的地方引用同一个对象时。
  • 当我需要切片而不复制它时。
  • 当我需要使用compiler intrinsics生成特定于CPU的指令,或解决编译器发出次优或天真代码的情况时。
  • 当我需要直接写入特定的内存区域时(因为它有内存映射的IO)。

答案 2 :(得分:5)

指针通常用于C ++。对它们感到满意,将有助于您了解更广泛的代码。也就是说,如果你可以避免使用它们,那么,随着程序变得越来越复杂,即使只是与其他库接口,你也可能需要它们。

  • 主要指针用于指代动态分配的内存(由new返回)。

  • 它们允许函数接受无法复制到堆栈的参数,因为它们太大或无法复制,例如系统调用返回的对象。 (我认为堆栈对齐也可能是一个问题,但过于朦胧而无法自信。)

  • 在嵌入式编程中,它们用于指代硬件寄存器之类的东西,它要求代码写入内存中非常的特定地址。

  • 指针也用于通过基类接口访问对象。那就是如果我有一个派生自A类class B : public A {}的B类。这是对象B的一个实例,可以通过将其地址提供给指向A类的指针来访问类A,即:A *a = &b_obj;

  • 使用指针作为数组上的迭代器是一个C语言。这在旧的C ++代码中可能仍然很常见,但可能被认为是STL迭代器对象的不良表兄。

  • 如果你需要与C代码接口,你将不可避免地需要处理用于引用动态分配对象的指针,因为有 no 引用。 C字符串只是指向由nul'\ 0'字符终止的字符数组的指针。

一旦你对指针感到满意,指针的指针似乎就不那么糟糕了。最明显的例子是main()的参数列表。这通常被声明为char *argv[],但我已经看到它(我认为合法地)宣称为char **argv

声明是C风格,但它说我有指向char的指针数组。这被解释为C样式字符串的任意大小的数组(大小由argc携带)(由nul'\ 0'字符终止的字符数组)。

答案 3 :(得分:4)

如果您不需要指针,我会花很多时间担心它们,直到需要时为止。

也就是说,指针有助于提高编程效率的主要方法之一是避免实际数据的复制。例如,假设您正在编写网络堆栈。您收到要处理的以太网数据包。您连续将堆栈中的数据从“原始”以太网驱动程序传递到IP驱动程序到TCP驱动程序,例如,将HTTP驱动程序传递给处理其包含的HTML的内容。

如果您正在为每个内容制作新内容的副本,那么您最终会在之前制作至少四个数据副本,而实际上它是完全渲染的。

使用指针可以避免很多 - 而不是复制数据本身,你只需传递一个指向数据的指针。网络堆栈的每个连续层查看其自己的标头,并将指针传递给它认为是“有效负载”的指针,直到堆栈中的下一个更高层。下一层查看自己的标头,修改指针以显示它认为有效负载的内容,并将其传递到堆栈上。所有四个层都使用一个真实数据副本,而不是四个数据副本。

答案 4 :(得分:2)

指针的一个重要用途是对数组进行动态调整。如果在编译时不知道数组的大小,则需要在运行时分配它。

int *array = new int[dynamicSize];

如果您对此问题的解决方案是使用STL中的std::vector,他们会在幕后使用动态内存分配。

答案 5 :(得分:2)

有几种情况需要指针:

  • 如果您正在使用虚拟方法的抽象基类。你可以持有一个std :: vector并遍历所有这些对象并调用一个虚方法。这需要指针。
  • 您可以将指向缓冲区的指针传递给从文件读取的方法等。
  • 您需要在堆上分配大量内存。

从一开始就关心内存问题是一件好事。因此,如果你开始使用指针,你可以看看智能指针,例如boost的shared_ptr。

答案 6 :(得分:2)

What are good scenarios where using pointers is a must?
访谈。实现strcpy。

What do they allow you to do that you couldn't do otherwise?
使用继承层次结构。二叉树等数据结构。

In which way to they make your programs more efficient?
它们为程序员提供了更多控制权,可以在运行时创建和删除资源。

And what about pointers to pointers???
常见的面试问题。你将如何在堆上创建二维数组。

答案 7 :(得分:1)

指针有一个特殊值NULL,该引用不会。我使用指针NULL是有效且有用的值。

答案 8 :(得分:1)

我只想说我很少使用指针。我使用引用和stl对象(deque,list,map等)。

一个好主意是当你需要返回一个调用函数应该释放的对象或者你不希望按值返回时。

 List<char*>* fileToList(char*filename) { //dont want to pass list by value
 ClassName* DataToMyClass(DbConnectionOrSomeType& data) { 
 //alternatively you can do the below which doesnt require pointers
 void DataToMyClass(DbConnectionOrSomeType& data, ClassName& myClass) { 

这几乎是我使用的唯一情况,但我并不认为这很难。此外,如果我想要一个函数来修改一个变量,并且不能使用返回值(比如我需要多一个)

 bool SetToFiveIfPositive(int**v) {

答案 9 :(得分:0)

您可以将它们用于链接列表,树木等。 它们是非常重要的数据结构。

答案 10 :(得分:0)

通常,指针很有用,因为它们可以保存一块内存的地址。它们在某些低级驱动程序中特别有用,它们可以有效地用于逐字节操作一块内存。它们是C ++从C继承的最强大的发明。

至于指向指针的指针,这里有一个“hello-world”示例,向您展示如何使用它。

#include <iostream>

void main()
{
    int i = 1;
    int j = 2;

    int *pInt = &i;                 // "pInt" points to "i"

    std::cout<<*pInt<<std::endl;    // prints: 1

    *pInt = 6;                      // modify i, i = 6

    std::cout<<i<<std::endl;        // prints: 6

    int **ppInt = &pInt;            // "ppInt" points to "pInt"

    std::cout<<**ppInt<<std::endl;  // prints: 6

    **ppInt = 8;                    // modify i, i = 8

    std::cout<<i<<std::endl;        // prints: 8

    *ppInt = &j;                    // now pInt points to j

    *pInt = 10;                     // modify j, j = 10

    std::cout<<j<<std::endl;        // prints: 10
}

正如我们所见,“pInt”是指向整数的指针,指向开头的“i”。有了它,你可以修改“我”。 “ppInt”是指针指向“pInt”的指针。有了它,你可以修改恰好是地址的“pInt”。结果,“* ppInt =&amp; j”使得“pInt”现在指向“j”。所以我们得到了上面的所有结果。