【问题标题】:What is the default constructor for C++ pointer?C++ 指针的默认构造函数是什么?
【发布时间】:2009-06-01 21:55:27
【问题描述】:

我有这样的代码:

class MapIndex
{
private:
    typedef std::map<std::string, MapIndex*> Container;
    Container mapM;

public:
    void add(std::list<std::string>& values)
    {
        if (values.empty()) // sanity check
            return;

        std::string s(*(values.begin()));
        values.erase(values.begin());
        if (values.empty())
            return;

        MapIndex *&mi = mapM[s];  // <- question about this line
        if (!mi)
            mi = new MapIndex();
        mi->add(values);
    }
}

我主要关心的是,如果将新项目添加到地图中,mapM[s] 表达式是否会返回对 NULL 指针的引用?

SGI docs 这么说:data_type& operator[](const key_type& k) 返回对与特定键关联的对象的引用。如果地图还没有包含这样的对象,则 operator[] 插入默认对象 data_type()。

所以,我的问题是插入 default object data_type() 是否会创建一个 NULL 指针,还是会创建一个指向内存中某处的无效指针?

【问题讨论】:

    标签: c++ dictionary pointers constructor


    【解决方案1】:

    它会创建一个NULL (0) 指针,无论如何这是一个无效的指针:)

    【讨论】:

    • 我不介意它无效,但希望它“安全”。您可以轻松检查 0 指针,并且可以在其上调用“删除”。任何参考(URL)来阅读这个?
    • 我手头没有参考资料。但我很确定指针构造函数会将其初始化为 0(就像所有整数类型一样,如 intshort、...)。
    • C++ 标准,8.5 第 5 段:“默认初始化 T 类型的对象意味着:否则(非 POD 或数组),对象为零初始化。”上面只有几行:'对 T 类型的对象进行零初始化意味着:如果 T 是标量类型(3.9),则将对象设置为转换为 T 的值 0(零);'在同一标准中,3.9 第 10 段:“算术类型 (3.9.1)、枚举类型、指针类型和指向成员类型的指针 (3.9.2),[...] 统称为标量类型。”所以是的,指针将默认初始化为 0。
    • 哦,顺便说一句,我检查了手头的当前标准,但是周围有一些草稿 (open-std.org/jtc1/sc22/wg21/docs/papers/2005/n1905.pdf),您可以在其中检查一些问题。您很有可能不会遇到最终标准与草案相差很大的黑点。该标准可以在线购买,价格约为 30 美元(pdf)
    • 这种行为在 C++0x 中不会改变,因为依赖这种行为的代码太多了。
    【解决方案2】:

    是的,它应该是一个零 (NULL) 指针,因为当 stl 容器未显式存储对象时(即访问地图中不存在的键或将向量调整为更大的大小),它们将默认初始化对象)。

    C++ 标准,8.5 第 5 段状态:

    默认初始化一个对象 类型 T 表示:

    • 如果 T 是非 POD 类类型(子句类),则默认 T 的构造函数被调用(并且 如果 T 有,则初始化是非良构的 没有可访问的默认构造函数)
    • 如果 T 是数组类型,则每个元素都是默认初始化的
    • 否则,对象的存储将被初始化为零。

    您还应该注意,默认初始化不同于简单地省略构造函数。当您省略构造函数并简单地声明一个简单类型时,您将获得一个不确定的值。

    int a; // not default constructed, will have random data 
    int b = int(); // will be initialised to zero
    

    【讨论】:

      【解决方案3】:

      更新:我完成了我的程序,而我所询问的那一行有时会导致它崩溃,但在稍后的阶段。问题是我正在创建一个新对象而不更改存储在 std::map 中的指针。真正需要的是对该指针的引用或指针。

      MapIndex *mi = mapM[s];  // <- question about this line
      if (!mi)
          mi = new MapIndex();
      mi->add(values);
      

      应该改为:

      MapIndex* &mi = mapM[s];  // <- question about this line
      if (!mi)
          mi = new MapIndex();
      mi->add(values);
      

      我很惊讶没有人注意到这一点。

      【讨论】:

      • 无论mi 是映射条目的副本还是引用,测试if (!mi) 的结果应该相同(即mi 为0)。不同之处在于后续分配实际上会更新映射条目,以防引用(这可能是需要的)。
      • 这个问题是可以回答的,无需深入探讨,所以它是(这不是真正的答案)。
      【解决方案4】:

      表达式data_type() 值初始化一个对象。对于具有默认构造函数的类类型,调用它;如果它不存在(或默认),例如指针,则该对象被零初始化。

      所以是的,您可以依靠您的地图创建一个NULL 指针。

      【讨论】:

        【解决方案5】:

        不确定崩溃,但肯定存在内存泄漏,如下所述:

        if (!mi) 
            mi = new MapIndex(); 
        

        总是返回 true,因为指针 mi 不是对 mapM 为特定值 s 持有的内容的引用。

        我也会避免使用常规指针并使用boost::shared_ptr 或一些 销毁时释放内存的其他指针。这允许您调用mapM.clear()erase(),它们应该调用存储在映射中的键和值的析构函数。好吧,如果值是 POD (例如您的指针),那么除非手动删除,否则不会为此调用析构函数,而遍历整个映射将导致内存泄漏。

        【讨论】:

          猜你喜欢
          • 2013-03-28
          • 2015-11-21
          • 2015-04-11
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2023-03-14
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多