3

我在这里使用“构造函数和析构函数”下的代码

基本上,我想将它与“指向类的指针”(下面一点)下的指针构造结合起来,我觉得它真的很整洁。

现在这有效:

int main () {
   CRectangle * prect;     //l2
   CRectangle rect (3,4);  //l3
   prect = ▭          //l4
   cout << "rect area: " << (*prect).area() << endl;
   return 0;
}

我的问题是,我们可以用一种更优雅的方法替换 l2-l4,而不需要在第 3 行创建 rect 吗?

4

3 回答 3

3

要创建不需要任何自动变量的对象(例如rect在上面的代码中),您必须使用new运算符。使用运算符创建的对象new存储在free store中,new表达式计算为指向新创建对象的指针。

现在,可以继续说,new操作符就是答案,仅此而已,但实际上不是:它没有回答用更优雅的解决方案替换这几行的问题,因为它不会是一个。

这个答案的其余部分与如何new使用有关。

与自动对象不同,存储在空闲存储中的对象不会自动销毁,而是它们的生命周期和销毁由delete操作员控制(您应该删除不再需要的对象以释放资源)。为确保销毁,您应该始终将new表达式中的指针存储到所谓的智能指针中。一个很好的、简单的规则:仅在智能指针构造函数中使用new运算符(除非您知道自己在做什么)。

C++11 中有几个智能指针,而标准的早期版本只定义了一个,auto_ptr. 也许是因为它的怪癖,或者仅仅因为它有一个替代品,它实际上在 C++11 中已被弃用,并且可能不应该在新代码中使用,至少在 C++11 中不应该(现在,这是一种观点) .

智能指针使用示例:

boost::shared_ptr<CRectangle> shared_rect(new CRectange(3, 4));
std::unique_ptr<CRectangle> rect(new CRectangle(3, 4)); // C++11 only

// use smart pointers like like regular pointers; indirection through * or ->
// i.e. (*rect).area() or rect->area()
于 2012-06-04T20:35:29.723 回答
3

我也喜欢@eq- 的回答,但除了动态内存分配之外,您还可以尝试放置新的。(当我说“尝试”时,我并不是说这是做事的方式,而是更像这样更值得深思。我不是想把你引向一个曲折的小通道迷宫,都一样。)

char mem[sizeof(CRectangle)] alignas(CRectangle);
CRectangle *prect = new (mem) CRectangle(3, 4); // construct
std::cout
    << "rect area: " << prect->area()
    << std::endl;
prect->~CRectangle();                           // destruct

在使用自定义分配器时,您通常会使用placement new。可以出于各种原因使用自定义分配器,但在我的工作中,它通常用于预分配策略(例如池分配器或具有严格内存配置的嵌入式系统)。在这种情况下,放置 new 用于初始化具有自动存储类的内存。与您的原始rect对象一样,当代码超出范围时,该对象的内存将变得无效。但是,CRectangle构造函数可能会执行需要清理的初始化,因此在内存超出范围之前会显式调用析构函数。

请注意,您不能使用auto_ptrfor prect,因为在指针上调用 delete 将是未定义的(因为内存不是动态创建的)。shared_ptr如果您将自定义删除器传递给构造函数,则可以使用。unique_ptr如果您在第二个模板参数中传入自定义删除器,则可以使用。自定义删除器只是对析构函数进行显式调用,以便智能指针在代码超出范围时自动进行清理工作。

template <typename T>
struct placement_delete {
    void operator () (T *t) const { t->~T(); }
};

char mem2[2][sizeof(CRectangle) alignas(CRectangle);
std::shared_ptr<CRectangle>
    sprect(new (mem2[0]) CRectangle(2, 3), placement_delete<CRectangle>());
std::unique_ptr< CRectangle, placement_delete<CRectangle> >
    uprect(new (mem2[1]) CRectangle(3, 4));

new调用可能会引发std::bad_alloc异常。由于它未被捕获,因此您的程序将在抛出异常并返回错误结果后立即终止。如果你想优雅地处理异常,你可以使用tryandcatch阻塞。

CRectangle *prect;
try {
    prect = new (mem) CRectangle(3, 4);
} catch (std::bad_alloc) {
    // do something about it?
    abort();
}

学习用,abort还行。它可以让您练习捕获异常,但可以让您在分析由于abort. (在 UNIX 上,这通常是一个核心文件。)

于 2012-06-04T21:28:59.947 回答
0

那这个怎么样?

int main () {
   CRectangle rect (3,4); // on stack
   cout << "rect area: " << rect.area() << endl;
   return 0;
}

或者如果你必须prect

int main () {
   CRectangle rect (3,4); // still on stack
   CRectangle * prect = &rect; // simple alias
   cout << "rect area: " << prect->area() << endl; // same as (*prect).area()
   return 0;
}

但至少在您的简单代码prect中是多余的。

通常人们会期望看到一些new CRectangle()(或派生自 的类CRectangle,在堆上分配)prect在创建时或以后分配。但目前还不是很清楚你打算做什么。但是由于在您的示例prect中只是 的别名rect,为什么不rect直接使用(我的第一段代码)?


使用堆:

int main () {
   CRectangle* prect = new CRectangle(3,4); // using heap
   // new will throw if out of memory, so no check for null pointer needed here
   cout << "rect area: " << prect->area() << endl; // same as (*prect).area()
   delete prect; // free the memory - needed because it's not a stack object
   return 0;
}

正如 Mooing Duck 在评论中提到的,STLstd::auto_ptr现在已被(在 C++11 中)取代。std::unique_ptr由于这个“智能指针”充当堆栈上的容器类,用于存放我们在堆上的类的指针,因此它也会进行清理

int main () {
   try
   {
     std::auto_ptr<CRectangle> prect(new CRectangle(3,4)); // using heap, via smart ptr
     // new will throw if out of memory, so no check for null pointer needed here
     cout << "rect area: " << prect->area() << endl; // same as (*prect).area()
   }
   catch(exception& e)
   {
     cout << "Exception caught: " << e.what() << endl;
   }
   return 0;
}

auto_ptr和之间的主要区别在于unique_ptr它们在所有权方面的语义。您可以在智能指针的 Wikipedia 页面上获得概述。

于 2012-06-04T20:25:31.757 回答