【问题标题】:What was the most useful time you used a pointer-to-pointer and/or reference-to-pointer in a c/c++ project?您在 c/c++ 项目中使用指针指针和/或指针引用最有用的时间是什么时候?
【发布时间】:2010-01-19 20:04:13
【问题描述】:

Pointers-to-pointers 和 references-to-pointers 一开始似乎过于复杂,尤其是对于新手而言。您在从事的 c/c++ 项目中使用它的最佳和最有用的原因是什么?

这可以帮助人们更好地理解指针以及如何有效地使用它们。

已编辑:包括 C 和 C++

【问题讨论】:

  • 每当您需要在函数参数中返回指针时。
  • 如果它旨在帮助人们,这应该是一个 wiki。
  • @wheaties:为什么需要一个 wiki 才能帮助他人?它不是很有帮助吗?
  • @sbi 这很有帮助,但从问题的措辞看来,这似乎是围绕 wiki 设计的,而不是回答特定问题。顺便说一句,这个问题的基础非常广泛,并且具有与 wiki 相同的氛围。
  • 我明白这一点,但 StackOverflow.com 很棒,我想在这里发布它,因为人们很棒,网站也很棒。

标签: c++ c pointers reference


【解决方案1】:

指向指针的指针非常常用作为分配和返回缓冲区的参数,例如字符串:

void SetValue(char** buf)
{
    string s = "Hello, Ptr Ref";
    *buf = new char[s.length()+1];
    copy(s.begin(), s.end(), *buf);
    (*buf)[s.length()] = 0;
}


int main()
{
    char* buf = 0;
    SetValue(&buf);
    cout << buf;
    delete [] buf;
    return 0;
}

同样,您可以传入对该缓冲区的引用,而不是传入指向要分配和修改的缓冲区的指针的指针。这可以帮助向调用者阐明函数的语义,这样他们就不会做诸如 call SetValue(0);

之类的事情
void SetValue(char*& buf)
{
    string s = "Hello, Ptr Ref";
    buf = new char[s.length()+1];
    copy(s.begin(), s.end(), buf);
    buf[s.length()] = 0;
}


int main()
{
    char* buf = 0;
    SetValue(buf);
    cout << buf;
    delete [] buf;
    return 0;
}

尽管SetValue(char*&amp;) 的语义可能比SetValue(char**) 版本更清晰一些,但仍有改进的余地。想到谁拥有结果指针的问题。

请记住,虽然这些示例过于简单和人为,但这样的实现比比皆是。在执行此操作的许多情况下,您别无选择——例如,当调用像FormatMessage() 这样的WINAPI 函数并要求它分配缓冲区时。但是在您编写函数的许多其他情况下,还有其他方法可以给猫剥皮。可以说,更好的方法。如:

string GimmeValue()
{
  return "Hello, String";
}

出于各种原因,这可能会更好。 (可能)在语义上按值返回值比使用指针对指针之类的异常值更清晰,因为所有权问题很容易解决。当堆栈分配的变量可以正常工作时,通常最好避免在某些地方使用 operator new,因为您可以避免由于delete缓冲区不止一次而导致潜在的内存泄漏或缺陷。

【讨论】:

  • 任何传入的 buffer (例如,char * 指向的数据)都可以修改(是否好主意,取决于实现细节) - 你需要一个指针如果您想完全返回一个新缓冲区,则指向一个指针,以便您可以修改指针本身。
  • @Ruddy:稍微澄清一下措辞。干杯。
  • 你是不是故意在“分配和修改”中输入“&”?我很困惑你是指“参考”还是“和”:)
【解决方案2】:

由于还没有人提到这一点,它们可能用作迭代器。

假设您有一个某种对象的列表。而且你需要以特定的顺序遍历其中的一些。

因此,您创建了一个数组,其中包含指向您需要遍历的对象的指针,然后对该数组进行排序。

指向该数组的迭代器是指向指针的指针。

std::sort对其进行排序需要一对迭代器,这意味着它需要一对指向指针的指针。

然后用std::for_each 遍历它也需要一对迭代器。

这就是我最后一次使用指向指针的上下文。 (实际上我有最后一层抽象,所以我最终得到了指向指针的指针,但这可能更不寻常)

【讨论】:

  • “指向该数组的迭代器是指向指针的指针。” – 为什么不只是一个指针? int*int[] 的迭代器,无需出汗,无需双重间接。
  • 在我的例子中,“原始”集合是一个定制的单链表。现在,我需要对该列表中的元素子集执行某些操作,并且我需要以与列表中存在的顺序不同的顺序执行这些操作。所以我创建了一个二级数​​组,其中包含指向我想要处理的特定对象的指针,然后我对 that 数组进行了排序。那是一个指针数组,为了对其进行排序,我需要一个迭代器对,或者指向其中两个指针的指针。
  • 重要的是不应该修改原始列表或以其他方式影响。它应该以相同的顺序包含相同的元素。复制该列表中的对象不是一种选择,也不能以任何其他方式修改该列表。
  • 非常有趣,到目前为止我认为这是我见过的最独特的解决方案。
  • 是的,这就是我想提及它的原因。人们总是提到通常的嫌疑人(通过函数参数或二维数组返回指向分配数据的指针)。我喜欢这个,因为一方面它不那么明显,但另一方面,它也没什么特别的。我们习惯于将指针用作迭代器,并且看到容器持有指针也很常见。如果那个容器是一个数组,那么迭代器就会变成双指针,可能程序员甚至没有意识到他正在使用它们。
【解决方案3】:

指针对指针?动态分配的二维数组!

【讨论】:

  • 我使用简单的指针。效果更好,并且可以说更容易(如果您希望尺寸真正动态!)。
  • 任何时候你想要一个动态分配的数组指针都是必不可少的。任何时候你也想要一个动态大小的数组(一个可以改变大小的数组)。
  • @Konrad 如果您的数组正在存储对象,那么最好使用指向指针的指针。这样您就可以移动对象而无需复制整个对象。
  • 老实说,我现在将 vector> 用于我的 2D 数组,但不久前指针对指针没有那么大的作用!
  • @Alcon:在不复制的情况下移动对象是什么意思?
【解决方案4】:

我几乎不记得我最后一次直接使用其中一个是什么时候了。大多数情况下,它们对于实现自己的容器类之类的事情很有用——例如,如果你想在链表中分配一个节点,你在节点之间有指针,如果你想修改一个已传递的指针对于您的函数,您需要一个引用或指向该指针的指针。

当然,您也可以使用指向指针的指针来创建伪二维数组。如果你真的需要一个方形数组,它们不是一个特别好的选择,但如果你想要一个“参差不齐的数组”(例如,一个字符串数组,每个字符串的长度可能不同),它就很有用。

指向指针的指针在 C 中几乎是不可避免的,但在 C++ 中,标准库已经有了容器类——例如,如果你想在 C 中创建一个动态大小的字符串的动态数组,几乎唯一的选择就是从char **,并动态分配碎片。在 C++ 中,您通常希望使用 std::vector&lt;std::string&gt; 来代替。当/如果您决定实现自己的容器时,您仍然会遇到它们,但这种情况相当罕见(至少您必须从原始指针等开始,而不是在现有容器之上构建)。

【讨论】:

  • +1 - 我几乎从不使用单个原始指针。智能指针和 stl/boost 容器几乎涵盖了它们的用途(实现智能指针或容器除外)
【解决方案5】:

传递代表 TIFF 文件内容的值数组以进行地理处理。

【讨论】:

    【解决方案6】:

    假设您想返回一个已分配的缓冲区 - 或者您可能需要重新分配一个传入的缓冲区。

    你可以返回一个指向缓冲区的指针,或者让调用者传入一个指针的地址(这样你就有了一个指向指针的指针),然后将该指针设置为指向你的缓冲区。

    【讨论】:

      【解决方案7】:

      COM - 任何返回接口的方法都必须传递一个指向指针参数的指针。

      【讨论】:

      • 是的。从根本上说,之所以如此,是因为被调用的函数(例如 CoCreateInstance)分配了对象。通常,WINAPI 处理此问题的方式是采用 ptr-to-ptr outval 参数。
      【解决方案8】:

      我曾经在一个函数中使用了一个 char ***arg 参数来更新一个动态分配的类似 argv 的数组,并把我的同事吓跑了。

      【讨论】:

        【解决方案9】:

        嗯,char **argv 和动态分配的二维锯齿状数组通常都非常有用。

        只要您希望允许被调用方更改指针(而不是指针指向的内容),对指针的引用就很有用。例如,动态提供内存的人:

        void feedMeSeymour(void *&p, int size) {
            p = new char[size];
        }
        

        【讨论】:

        • 二维锯齿数组可以是vector&lt;vector&lt;&gt;&gt;,像argv这样的结构可以是vector&lt;string&gt;...
        【解决方案10】:

        指向指针的指针也适用于创建混合长度数组的数组。我认为它们通常被称为 rigid 锯齿状数组。我以前用它来减少嵌入式系统上的内存使用量。

        【讨论】:

        • std::vector 做得更干净。如果你不想要它额外的回旋余地,你可能最好找到另一个容器类(也许在 Boost 中),而不是用 C 方式来做。
        【解决方案11】:

        模板化排序向量的排序函数。这是一个比较现实的例子:

        template <typename T>
        class Vector {
        public:
            typedef bool SortFunc(const T& left, const T& right) ;
            void SetSort(SortFunc* pFunc){}
        };
        
        
        bool intSort(const int& left, const int& right)
        {
            return true;
        }
        
        bool pintSort( int* const & left, int* const & right)
        {
            return true;
        }
        
        //in main
        
        Vector<int> intVec;
        Vector<int*> pintVec;
        
        intVec.SetSort(&intSort);
        pintVec.SetSort(&pintSort);
        

        考虑到我们可以将任何东西作为向量参数传递,使用 const 引用作为排序函数参数是相当明智的。但不理智的是,正在研究如何将血腥的星号、& 和 consts 串起来,以实现 对非 const 整数指针的 const 引用

        【讨论】:

          猜你喜欢
          • 2010-10-14
          • 2015-10-31
          • 2015-02-13
          • 1970-01-01
          • 2010-12-06
          • 1970-01-01
          • 2020-08-19
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多