【问题标题】:In C++, is not permitted to pass an object to function directly?在 C++ 中,不允许将对象直接传递给函数吗?
【发布时间】:2011-10-19 08:01:23
【问题描述】:

我知道 C,但我不擅长 C++。

下面的代码会崩溃(在getval()中,使用引用作为参数是可以的)。 并且*p 的值在第一个cout 语句之后更改。看起来内存溢出导致了一些覆盖。

我的问题是它为什么会崩溃(或者为什么它的值会改变)。 它是对象的“按值调用”,所以它应该工作吗?

class myclass { 
  int *p; 

  public: 
    myclass(int i); 
    ~myclass() { delete p; } 
    int getval(myclass o); 
}; 

myclass::myclass(int i) 
{ 
  p = new int; 

  if (!p) { 
    cout << "Allocation error\n"; 
    exit(1); 
  } 

  *p = i; 
}

int myclass::getval(myclass o) 
{ 
  return *o.p; 
} 

int main() 
{ 
  myclass a(1), b(2); 

  cout << a.getval(a) << " " << a.getval(b) << endl; 
  cout << b.getval(a) << " " << b.getval(b) << endl; 

  return 0; 
} 

【问题讨论】:

  • 提示:当你的对象被复制并且两个副本都被破坏时会发生什么?

标签: c++ crash runtime-error pass-by-value


【解决方案1】:

阅读 the rule of three

基本上,您的代码不支持按值复制。

因此动态分配的内存被过早地释放(通过析构函数)。

干杯,

【讨论】:

    【解决方案2】:

    我的问题是它为什么会崩溃(或者为什么它的值会改变)。

    这是浅拷贝重复删除很常见的问题。编译器选择默认的复制构造函数,a.po.p 都指向相同的内存位置。当两个对象都调用它们的析构函数时,delete p; 语句将被执行两次。多次释放相同的内存是一种未定义的行为,它会导致系统崩溃。

    这是对象的“按值调用”,所以它应该工作吗?

    如果编码正确,那么是的它会起作用。制作p 内容的深层副本,然后它应该可以工作。但是,最好通过引用传递,直到可能为止。

    【讨论】:

      【解决方案3】:

      就 C++ 语言而言,您正在做的事情是允许的。但这真的是非常糟糕的编程。它不想评论代码的每一行。相反,我重新编写了整个代码,您将这段代码与您的代码进行比较,还可以看到代码中嵌入的 cmets:

      class myclass { 
        int p;  //no pointer, as it is not needed
      
        public: 
          myclass(int i) : p(i) {} //use member initialization-list 
          int getval() const  //no parameter, and make the function const
          {
               return p;
          }
      }; 
      
      int main() 
      { 
        myclass a(1), b(2); 
      
        cout << a.getval() << endl;
        cout << b.getval() << endl; 
        return 0; 
      } 
      

      【讨论】:

      • 就我个人而言,我认为这个例子实际上更复杂,这是一个罕见的有能力提出问题并将代码缩减为最小重现案例的例子......!
      • 这段代码看起来很干净。我在回答时没有注意到这一点。不错。
      【解决方案4】:

      您将 myclass 作为值传递,因此创建了副本。当 myclass::getval 返回时,堆栈展开并调用 myclass 析构函数,释放指针指向的内存在另一个对象中。

      【讨论】:

        【解决方案5】:

        您在这里双重删除 p,如果您不想进行深度复制,共享指针可能会很有用。只要可能且有意义,请尝试使用自动变量(int 而不是 int*)

        【讨论】:

          【解决方案6】:

          你还没有实现拷贝构造函数(如果你这样做了,记住三法则),所以当拷贝被销毁时,你会多次释放相同的内存。

          提示:

          实现复制构造函数:

          myclass(const myclass& other)
          {
             p = new int;
             *p = *other.p;
          }
          

          通过引用传递:

          int getval(const myclass& o); 
          

          一个好的规则也是使不依赖于对象本身的函数成为静态的(在这种特殊情况下这对您没有帮助,但仍然有用)。由于getval 不访问任何对象成员,您可以将其设为静态。

          崩溃的原因:

          当您调用getval(o) 时,会创建o 的副本,并且它的成员指向与原始对象相同的位置,因为您没有声明复制构造函数。副本被破坏,内存被删除。原始对象已损坏。

          【讨论】:

            猜你喜欢
            • 2016-02-07
            • 2015-08-30
            • 1970-01-01
            • 1970-01-01
            • 2015-02-01
            • 1970-01-01
            • 1970-01-01
            • 2019-11-15
            • 2010-10-14
            相关资源
            最近更新 更多