【问题标题】:Class A initialisation within class B containing pointer to class AB 类中的 A 类初始化,包含指向 A 类的指针
【发布时间】:2012-05-01 16:25:31
【问题描述】:

我希望类 A 包含一些数据,而类 B 将包含指向该数据的指针。我通过返回对数据对象 A 的引用的函数提供对数据的访问。如果创建对象 B,则可以访问对象 A,但是如果创建指向 B 的指针,则等效操作会产生分段错误。像这样:

#include <iostream>
#include <vector>

class A {
public:
    A(const int pInt) {mInt = pInt;}
    void print() {std::cout << mInt << std::endl;}
private:
    int mInt; //some data
};

class B {
public:
   B() {mP1 = new A(1);} //initialise to 1
   ~B() {delete mP1;}
   A& access() {return *mP1;} //return reference to the data

private:
    A* mP1; //pointer to some data
};

int main() {
    B vB;
    vB.access().print(); //this works.

    B *vBptr;
    vBptr->access().print(); //Segmentation fault!

    std::vector<B*> vVec;
    vVec.resize(1);
    vVec[0]->access().print(); //Segmentation fault!
}

我猜在创建 B *vBptr 时 B 对象没有被初始化? 那么如何创建一个指向自动初始化的 B 对象的指针向量呢?

干杯。

【问题讨论】:

    标签: c++ pointers vector initialization


    【解决方案1】:

    你需要初始化指针:

    B *vBptr = new B;
    vBptr->access().print();
    

    同样适用于您的向量 - 您必须遍历元素并初始化它们。

    for ( int i = 0 ; i < vVec.size() ; i++ )
       vVec[i] = new B;
    

    请注意,您必须手动释放内存

    delete vBptr;
    

    for ( int i = 0 ; i < vVec.size() ; i++ )
       delete vVec[i];
    

    不要遇到内存泄漏。

    【讨论】:

    • 如何创建一个初始化的 B* 向量?
    • @RobSimpson 我不认为你可以自动。但是遍历它并初始化组件就足够了。
    • 当你创建一个指向该对象的指针时,有没有办法默认自动构造一个对象?
    • @RobSimpson 如果你有一个对象向量,它们会自动构建。 但是你可以使用一个包装类,并拥有它的值,有点像智能指针。
    【解决方案2】:

    你的程序中有两个问题

    B *vBptr;
    

    您的指针尚未初始化,虽然它是 B 类型的指针,但未指向 B 类型的有效对象。将其更改为

    B *vBptr = new B();
    
    vVec.resize(1);
    

    这里调整大小不会为向量的每个元素分配存储空间。您可以通过将初始化提供给向量的 resize 方法来使用实际对象初始化元素

    vVec.resize(1,new B());
    

    【讨论】:

    • 酷,我不知道你能做到这一点。 +1
    • 酷,当数组超出范围时释放内存怎么办?这是自动处理的吗?
    • 只是警告如果 resize(1, new B()); 更改为 resize(2, new B()) 向量中将有两个元素指向 B 的同一实例。
    • @RobSimpson:不,不会。毕竟它是指针而不是对象的向量。
    • @hmjd 很酷,我也不知道。然后...我猜这不是一个安全的解决方案...
    【解决方案3】:

    您实际上需要将指针设置为:

    B *vBptr;          // bad -- uninitialized 
    B *vBptr = new B;  // proper
    

    【讨论】:

      【解决方案4】:

      正如其他答案中提到的,指针变量不会自动为其分配内存,因此您需要手动分配和释放内存,如下所示:

      B vB;
      vB.access().print(); //this works.
      
      B *vBptr = new B;//allocate memory for vBptr
      vBptr->access().print();
      delete vBptr;//clean up of vBptr
      std::vector<B*> vVec;
      vVec.push_back(new B);
      vVec[0]->access().print(); 
      delete vVec[0];
      

      但是,我建议不要使用原始指针管理内存,因为这样很容易导致内存泄漏和错误。例如。如果vBptr-&gt;access().print() 抛出异常,上面的代码会泄漏内存。您的B 类违反了三规则(手动管理资源时的默认复制构造函数/赋值操作)。因此,如果您复制 B 对象,将会发生可怕的事情(准确地说是双重释放并访问已删除的内存)。

      因此,我建议您使用某种智能指针。如果你使用 c++11,你可以使用std::shared_ptrstd::unique_ptr,否则它们是std::tr1::shared_ptr。如果你的编译器没有 tr1,boost 也有boost:shared_ptr。那么您的代码将如下所示:

      class B {
      public:
         B():mP1(new A(1)) {} //initialise to 1
         ~B() {} //no extra managing necessary
         A& access() {return *mP1;} //return reference to the data
      
      private:
          std::shared_ptr<A> mP1; //pointer to some data
      };
      
      int main() {
          B vB;
          vB.access().print(); //this works.
      
          std::shared_ptr<B> vBptr(new B);
          vBptr->access().print();
      
          std::vector<std::shared_ptr<B> > vVec;
          vVec.push_back(std::shared_ptr<B>(new B));
          vVec[0]->access().print(); 
      }
      

      如果你需要最小的开销并且使用 c++11 std::unique_ptr 是你的朋友,否则 iirc boost 有 boost::unique_ptr。远离std::auto_ptr。除非您喜欢增加的复杂性/潜在的错误,或者您的代码对性能非常敏感(并且使用非常高效的自定义分配器),否则几乎没有理由手动管理您的内存。

      【讨论】:

        【解决方案5】:
        B *vBptr;
        vBptr->access().print(); //Segmentation fault!
        

        嗯,当然;您创建了一个变量来存储指向 B 对象的指针,但实际上并没有让它指向 B 对象。 (提示:查看您为 B 类编写的代码,您将 B 的 A* mP1 指向某物?)

        std::vector<B*> vVec;
        vVec.resize(1);
        vVec[0]->access().print(); //Segmentation fault!
        

        嗯,当然;您创建了一个指向 B 对象的指针向量,然后调整向量的大小以添加一个元素;该元素是默认构造的指向 B 的指针。对于指针类型,默认构造是空指针,因此您也没有指向任何实际的 B 对象。

        您的问题与任何 A 或 B 实现无关。

        那么我怎样才能创建一个指向 B 对象的指针向量

        你认为你为什么要这样做?只制作 B 个对象的向量有什么问题?

        【讨论】:

        • B 是我实现中的抽象基类。
        • 奇怪的是,你似乎在任何地方都没有virtual
        • 不想把真正的问题复杂化。
        • 无论如何,如果指针的原因是允许多态性(存储从 B 派生的异构集合),那么您真的应该研究某种形式的智能指针,或者使用boost::ptr_vector 或类似的。自己管理指针是一件令人头疼的事情,而且会浪费时间。
        【解决方案6】:
        B vB;
        vB.access().print();
        
        B *vBptr = &vB; // or B *vBptr = new B;
        vBptr->access().print();
        
        std::vector<B*> vVec;
        vVec.push_back(&vB);  // vVec.push_back(new B);
        vVec[0]->access().print(); 
        

        但是,B NOT 有必要这样写吗?

        class B {
         public:
          //   B() {}
          //   ~B() {}
          A& access() {return a;}
        
        private:
           A a;};
        

        【讨论】:

          【解决方案7】:

          感谢所有做出贡献的人。以下是我现在采用的实现,即使用 boost::shared_ptr:

          #include <iostream>
          #include <vector>
          #include "boost/smart_ptr.hpp"
          
          class A {
          public:
              A(const int pInt) {mInt = pInt;}
              void print() const {std::cout << mInt << std::endl;}
              void set(const int pInt) {mInt = pInt;}
          private:
              int mInt; //some data
          };
          
          class B {
          public:
              B() {}  //leave A pointer as null
              ~B() { } //delete handled by shared_ptr
              A& access() {return *mP1;} //return reference to the data
              boost::shared_ptr<A>& access_A_ptr() {return mP1;} //return the pointer for assignment
          
          private:
              boost::shared_ptr<A> mP1;
          };
          
          int main() {
          
              std::vector< boost::shared_ptr<A> > vVecA; //data to be shared
              for (unsigned int i = 0; i < 5; i++) {
                  boost::shared_ptr<A> vAptr(new A(i));
                  vVecA.push_back(vAptr);
                  vVecA[i]->print();
              }
          
              vVecA[2]->set(123);
          
              for (unsigned int i = 0; i < vVecA.size(); i++) {
                  vVecA[i]->print(); //changes to the underlying objects are reflected
              }
          
              boost::shared_ptr<B> vBptr(new B); //make an empty B
              vBptr->access_A_ptr() = vVecA[2]; //assignment of shared pointer
          
              vBptr->access().print();
          
              std::cout << "use count of vVecA[1] = " << vVecA[1].use_count() << std::endl; // = 1
              std::cout << "use count of vVecA[2] = " << vVecA[2].use_count() << std::endl; // = 2
          
              std::vector< boost::shared_ptr<B> > vVecB;
          
              //vVecB.resize(vVecA.size(), boost::shared_ptr<B> (new B) ); //resize and init to a SINGLE B object, NO!!!
          
              for (unsigned int i = 0; i < vVecA.size(); i++) {
                  vVecB.push_back(boost::shared_ptr<B> (new B)); //filling array with separate empty Bs
                  vVecB[i]->access_A_ptr() = vVecA[i];
                  vVecB[i]->access().print(); // = 0,1,123,3,4
              }
          
              vVecA[2]->set(2); //changes to A objects reflected in the B objects
          
              for (unsigned int i = 0; i < vVecB.size(); i++) {
                  vVecB[i]->access().print(); // = 0,1,2,3,4
              }
          
              std::cout << "use count of vVecA[1] = " << vVecA[1].use_count() << std::endl; // = 2
              std::cout << "use count of vVecA[2] = " << vVecA[2].use_count() << std::endl; // = 3
          
          }
          

          这实际上已成为共享指针的练习。但是,仍然非常欢迎就任何方面发表评论。

          谢谢!

          【讨论】:

            猜你喜欢
            • 2011-09-14
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2013-09-07
            • 1970-01-01
            • 2011-06-24
            • 1970-01-01
            相关资源
            最近更新 更多