代码之家  ›  专栏  ›  技术社区  ›  Nick Bolton

我必须为C++类字段使用指针吗?

  •  13
  • Nick Bolton  · 技术社区  · 16 年前

    在阅读问题之后 the difference between pointers and references ,我决定对类字段使用引用而不是指针。然而,这似乎是不可能的,因为它们不能声明为未初始化(对吗?).

    在我目前正在研究的特定场景中,我不想使用正态变量(顺便问一下,它们的正确术语是什么?)因为当我声明它们时它们会自动初始化。

    在我的代码段中,bar1自动用默认构造函数(这不是我想要的)实例化,而bar2会导致编译器错误,因为您不能使用未初始化的引用(正确吗?)和*bar3一样高兴,因为指针可以声明为未初始化(顺便问一下,将其设置为空是最佳实践吗?).

    class Foo
    {
    public:
        Bar bar1;
        Bar &bar2;
        Bar *bar3;
    }
    

    看起来在这个场景中我必须使用指针,这是真的吗?另外,使用变量的最佳方法是什么?语法有点麻烦…运气不好?怎么样 smart pointers 等?这相关吗?

    更新1:

    在我的类中尝试实现一个引用变量字段并在构造函数中初始化它之后,为什么我会收到以下错误?

    ../src/textures/VTexture.cpp: In constructor ‘vimrid::textures::VTexture::VTexture()’:
    ../src/textures/VTexture.cpp:19: error: uninitialized reference member ‘vimrid::textures::VTexture::image’
    

    这是真正的代码:

    // VTexture.h
    class VTexture
    {
    public:
        VTexture(vimrid::imaging::ImageMatrix &rImage);
    private:
        vimrid::imaging::ImageMatrix ℑ
    }
    
    // VTexture.cpp
    VTexture::VTexture(ImageMatrix &rImage)
        : image(rImage)
    {
    }
    

    我也试过在标题中这样做,但没有运气(我得到同样的错误)。

    // VTexture.h
    class VTexture
    {
    public:
        VTexture(vimrid::imaging::ImageMatrix &rimage) : image(rImage) { }
    }
    

    更新2:

    弗莱德拉尔森 -是的!有一个默认的构造函数;我忽略了它,因为我认为它与问题无关(我太傻了)。删除默认构造函数后,我导致了一个编译器错误,因为类与std::vector一起使用,后者要求有一个默认构造函数。所以看起来我必须使用一个默认的构造函数,因此必须使用一个指针。羞耻…或者是?:)

    6 回复  |  直到 16 年前
        1
  •  15
  •   Brian R. Bondy    16 年前

    问题1的答案:

    然而,这似乎是不可能的,因为它们[引用]不能声明为未初始化(对吗?).

    正确的。


    问题2的答案:

    在我的代码段中,bar1自动 用默认值实例化 构造器(这不是我想要的) &BAR2导致编译器错误,原因是 不能使用未初始化的引用 (正确吗?),

    class Foo
    {
    public:
        Foo(Bar &rBar) : bar2(rBar), bar3(NULL)
        {
        }
    
        Bar bar1;
        Bar &bar2;
        Bar *bar3;
    }
    

    在特定的情况下,我正在工作 现在,我不想用 正态变量(正确的是什么 顺便问一下,它们的术语是什么?)

    它们没有正确的名称,通常您可以说大多数讨论的指针(除了这个),您需要讨论的所有内容也将应用于引用。通过initailizer列表以相同的方式初始化非指针、非引用成员。

    class Foo
    {
    public: 
      Foo()  : x(0), y(4)
      {
      }
    
      int x, y;
    };
    

    问题4的答案:

    指针不能声明为未初始化 (顺便问一下,最好的做法是 设置为空?).

    它们可以声明为未初始化是。最好将它们初始化为空,因为这样您就可以检查它们是否有效。

    int *p = NULL;
    //...
    
    //Later in code
    if(p)
    {
      //Do something with p
    }
    

    问题5的答案:

    看来我得用指针了 在这种情况下,这是真的吗?也, 最好的使用方法是什么 变量?

    可以使用指针或引用,但不能重新分配引用,引用不能为空。指针和任何其他变量一样,比如int,但它拥有一个内存地址。数组是另一个变量的别名。

    指针有它自己的内存地址,而数组应该被视为共享它引用的变量的地址。

    对于引用,在初始化和声明它之后,使用它就像使用它引用的变量一样。没有特殊的语法。

    使用指针,要访问它保存的地址的值,必须 dereference 指针。你可以把*放在前面。

    int x=0;
    int *p = &x;//p holds the address of x
    int &r(x);//r is a reference to x
    //From this point *p == r == x
    *p = 3;//change x to 3
    r = 4;//change x to 4
    //Up until now
    int y=0;
    p = &y;//p now holds the address of y instead.
    

    问题6的答案:

    智能指针等怎么样?是 这是相关的吗?

    使用智能指针(请参阅boost::shared_ptr),以便在堆上分配时,不需要手动释放内存。我上面给出的例子都没有在堆上分配。下面是一个使用智能指针的例子。

    void createANewFooAndCallOneOfItsMethods(Bar &bar)
    {
        Foo *p = new Foo(bar);  
        p->f();
        //The memory for p is never freed here, but if you would have used a smart pointer then it would have been freed here.  
    }
    

    问题7的答案:

    更新1:

    在尝试实现 我的类中的引用变量字段 并在中初始化 建设者,为什么我会收到 以下错误?

    问题是您没有指定初始值设定项列表。请看我对上述问题2的回答。结肠后的一切:

    class VTexture
    {
    public:
        VTexture(vimrid::imaging::ImageMatrix &rImage)
          : image(rImage)
        { 
        }
    private:
        vimrid::imaging::ImageMatrix ℑ
    }
    
        2
  •  12
  •   Fred Larson    16 年前

    它们可以初始化。您只需要使用成员初始值设定项列表。

    Foo::Foo(...) : bar1(...), bar2(...), bar3(...)
    {
      // Whatever
    }
    

    最好用这种方式初始化所有成员变量。否则,除了原始类型,C++将用默认构造函数初始化它们。在大括号内分配它们实际上是重新分配它们,而不是初始化它们。

    另外,请记住,成员初始值设定项列表指定如何初始化成员变量,而不是顺序。成员按声明顺序初始化,而不是按初始值设定项的顺序初始化。

        3
  •  2
  •   deft_code    16 年前

    使用 null object design pattern

    我在用 int 但任何类型都是一样的。

    //header file
    class Foo
    {
    public:
       Foo( void );
       Foo( int& i );
    private:
       int& m_int;
    };
    
    //source file
    static int s_null_Foo_m_i;
    
    Foo::Foo( void ) :
       m_i(s_null_Foo_m_i)
    { }
    
    Foo::Foo( int& i ) :
       m_i(i)
    { }
    

    现在,您必须确保在构造默认值时foo是有意义的。您甚至可以检测到FOO是何时被默认构造的。

    bool Foo::default_constructed( void )
    {
       return &m_i == &s_null_Foo_m_i;
    }
    

    我完全同意这种观点,总是喜欢引用而不是指点。有两个值得注意的情况是,你无法摆脱一个参考会员:

    1. 空值有意义。

      这可以通过空对象设计模式来避免。

    2. 该类必须是可分配的。

      编译器不会为具有引用成员的类生成赋值运算符。您可以自己定义一个引用,但不能更改引用的绑定位置。

        4
  •  1
  •   J.W.    16 年前

    定义条形图和条形图时,也会产生副作用。*

    class Foo
    {
    public:
        Bar bar1; // Here, you create a dependency on the definition of Bar, so the header //file for bar always needs to be included.
        Bar &bar2;
        Bar *bar3; //Here, you create a pointer, and a forward declaration is enough, you don't have to always include the header files for Bar , which is preferred.
    }
    
        5
  •  0
  •   Pontus Gagge    16 年前

    仅仅因为->语法繁琐而使用引用并不是最佳原因…引用在指针上有一个很好的宣传,即空值不可能不使用技巧,但在初始化时也有缺点,并且有意外非法绑定临时值的风险,这些临时值随后会超出范围(例如,在隐式转换之后)。

    是的,智能指针(如boost指针)对于处理复合成员几乎总是正确的答案,而对于关联成员(shared_ptr)则偶尔也是正确的答案。

        6
  •  0
  •   Dan Breslau    16 年前
    class Foo {
    public:
        Bar bar1;
        Bar &bar2;
        Bar *bar3;
    
        // member bar2 must have an initializer in the constructor
        Bar::Bar(Bar& _bar2) : bar1(), bar2(_bar2), bar3(new Bar()) {}
    
        Bar::~Bar() {delete bar3;}
    }
    

    注意 bar2 不只是在ctor中初始化;它是用 bar 作为引用参数传入的对象。那个物体和 BAR2 田野将为新的生命而捆绑在一起 Foo 对象。这通常是一个非常糟糕的主意,因为很难确保这两个对象的生命周期能够很好地协调(即,在处理传入的bar对象之前,您永远不会处理传入的bar对象)。 对象。

    这就是为什么它非常喜欢使用任一实例变量(如 bar1 )或指向堆上分配的对象的指针(如 bar3 )