为什么不使用C ++中的一切指针? [英] Why not use pointers for everything in C++?

查看:115
本文介绍了为什么不使用C ++中的一切指针?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

假设我定义了一些类:

  class Pixel {
public:
Pixel {x = 0; y = 0;};
int x;
int y;
}

然后使用它编写一些代码。为什么要这样做?

  Pixel p; 
p.x = 2;
p.y = 5;

来自Java世界我总是写:

  Pixel * p = new Pixel(); 
p-> x = 2;
p-> y = 5;

他们基本上做同样的事情,对吗?
一个在堆栈上,另一个在堆上,所以我以后必须删除它。两者之间有什么根本区别吗?为什么我应该更喜欢一个呢?

解决方案

是的,一个在堆栈上,有两个重要的区别:




  • 首先,显而易见的,不那么重要的:堆分配很慢。堆栈分配速度很快。

  • 其次,更重要的是 RAII 。因为堆栈分配的版本被自动清理,所以它是有用的。它的析构函数被自动调用,它允许你保证任何由类分配的资源被清理。这是必要的如何避免内存泄漏在C ++。你避免他们从不调用 delete 自己,而是将它包装在堆栈分配的对象中调用 delete 他们的析构。如果您尝试手动跟踪所有分配,并在正确的时间调用 delete ,我保证您每100行代码至少有一个内存泄漏。



作为一个小例子,考虑下面的代码:

  class Pixel {
public:
Pixel(){x = 0; y = 0;};
int x;
int y;
};

void foo(){
Pixel * p = new Pixel();
p-> x = 2;
p-> y = 5;

bar();

delete p;
}

漂亮的无辜的代码,对不对?我们创建一个像素,然后我们调用一些不相关的函数,然后我们删除像素。是否有内存泄漏?



答案是可能。如果 bar 抛出异常会发生什么? delete 永远不会被调用,像素永远不会被删除,我们泄漏内存。现在考虑这个:

  void foo(){
Pixel p;
p.x = 2;
p.y = 5;

bar();
}

这不会泄漏内存。当然在这个简单的情况下,一切都在栈上,所以它被自动清理,但即使 Pixel 类在内部做了一个动态分配,那不会泄漏。 Pixel 类只会被赋予一个析构函数来删除它,并且这个析构函数会被调用,无论我们如何离开 foo 函数。即使我们离开它,因为 bar 抛出异常。下面的例子展示了这一点:

  class Pixel {
public:
Pixel x = new int(0); y = new int(0);};
int * x;
int * y;

〜Pixel(){
delete x;
delete y;
}
};

void foo(){
Pixel p;
* p.x = 2;
* p.y = 5;

bar();
}

Pixel类现在内部分配一些堆内存,但它的析构函数清理它,所以当使用类时,我们不必担心。 (我可能会提到,这里的最后一个例子被简化了很多,为了显示一般的原则。如果我们实际使用这个类,它包含几个可能的错误,如果y的分配失败,x永远不会释放,如果Pixel被复制,我们最终会试图删除相同的数据,所以最后一个例子是一个盐,真实世界的代码有点棘手,但它显示了一般的想法) / p>

当然,相同的技术可以扩展到除内存分配之外的其他资源。例如,它可以用于保证文件或数据库连接在使用后关闭,或者您的线程代码的同步锁被释放。


Suppose that I define some class:

class Pixel {
    public:
      Pixel(){ x=0; y=0;};
      int x;
      int y;
}

Then write some code using it. Why would I do the following?

Pixel p;
p.x = 2;
p.y = 5;

Coming from a Java world I always write:

Pixel* p = new Pixel();
p->x = 2;
p->y = 5;

They basically do the same thing, right? One is on the stack while the other is on the heap, so I'll have to delete it later on. Is there any fundamental difference between the two? Why should I prefer one over the other?

解决方案

Yes, one is on the stack, the other on the heap. There are two important differences:

  • First, the obvious, and less important one: Heap allocations are slow. Stack allocations are fast.
  • Second, and much more important is RAII. Because the stack-allocated version is automatically cleaned up, it is useful. Its destructor is automatically called, which allows you to guarantee that any resources allocated by the class get cleaned up. This is essentialy how you avoid memory leaks in C++. You avoid them by never calling delete yourself, instead wrapping it in stack-allocated objects which call delete internally, typicaly in their destructor. If you attempt to manually keep track of all allocations, and call delete at the right times, I guarantee you that you'll have at least a memory leak per 100 lines of code.

As a small example, consider this code:

class Pixel {
public:
  Pixel(){ x=0; y=0;};
  int x;
  int y;
};

void foo() {
  Pixel* p = new Pixel();
  p->x = 2;
  p->y = 5;

  bar();

  delete p;
}

Pretty innocent code, right? We create a pixel, then we call some unrelated function, and then we delete the pixel. Is there a memory leak?

And the answer is "possibly". What happens if bar throws an exception? delete never gets called, the pixel is never deleted, and we leak memory. Now consider this:

void foo() {
  Pixel p;
  p.x = 2;
  p.y = 5;

  bar();
}

This won't leak memory. Of course in this simple case, everything is on the stack, so it gets cleaned up automatically, but even if the Pixel class had made a dynamic allocation internally, that wouldn't leak either. The Pixel class would simply be given a destructor that deletes it, and this destructor would be called no matter how we leave the foo function. Even if we leave it because bar threw an exception. The following, slightly contrived example shows this:

class Pixel {
public:
  Pixel(){ x=new int(0); y=new int(0);};
  int* x;
  int* y;

  ~Pixel() {
    delete x;
    delete y;
  }
};

void foo() {
  Pixel p;
  *p.x = 2;
  *p.y = 5;

  bar();
}

The Pixel class now internally allocates some heap memory, but its destructor takes care of cleaning it up, so when using the class, we don't have to worry about it. (I should probably mention that the last example here is simplified a lot, in order to show the general principle. If we were to actually use this class, it contains several possible errors too. If the allocation of y fails, x never gets freed, and if the Pixel gets copied, we end up with both instances trying to delete the same data. So take the final example here with a grain of salt. Real-world code is a bit trickier, but it shows the general idea)

Of course the same technique can be extended to other resources than memory allocations. For example it can be used to guarantee that files or database connections are closed after use, or that synchronization locks for your threading code are released.

这篇关于为什么不使用C ++中的一切指针?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆