我正在使用“构造函数和析构函数”here
下的代码基本上,我想把它与下面的指针构造结合起来 “指向课堂的指针”(稍微低于一点)我发现它非常整洁。
现在可行:
int main () {
CRectangle * prect; //l2
CRectangle rect (3,4); //l3
prect = ▭ //l4
cout << "rect area: " << (*prect).area() << endl;
return 0;
}
我的问题是,我们可以用更优雅的方法取代l2-l4 不需要在第3行创建rect?
答案 0 :(得分:3)
要创建一个对象而不需要任何自动变量(例如上面代码中的rect
),您必须使用new
运算符。使用new
运算符创建的对象存储在免费存储中,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()
答案 1 :(得分:3)
我也喜欢@eq - 的答案,但除了动态内存分配外,您还可以尝试放置新内容。 (当我说&#34;尝试&#34;,我并不意味着这是做事的方式,但更像这样更值得思考。我不是想把你带到一个曲折的迷宫中小段落,都一样。)
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。可以出于各种原因使用自定义分配器,但在我的工作中,它通常用于预分配策略(如池分配器或具有严格内存供应的嵌入式系统)。在这种情况下,placement new用于初始化具有自动存储类的内存。与原始rect
对象一样,当代码超出范围时,对象的内存将变为无效。但是,CRectangle
构造函数可能会执行需要清理的初始化,因此在内存超出范围之前会对析构函数进行显式调用。
注意,您不能将auto_ptr
用于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
个异常。由于它未被捕获,因此在抛出异常并且出现错误结果后,程序将立即终止。如果您想要优雅地处理异常,可以使用try
和catch
块。
CRectangle *prect;
try {
prect = new (mem) CRectangle(3, 4);
} catch (std::bad_alloc) {
// do something about it?
abort();
}
对于学习,abort
没问题。它可以帮助您练习捕获异常,但在分析abort
导致的错误状态时,可以快速查明问题的位置。 (在UNIX上,这通常是核心文件。)
答案 2 :(得分: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 = ▭ // 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在评论中提到的那样,STL已经std::auto_ptr
取代std::unique_ptr
(在C ++ 11中)。由于这个“智能指针”作为堆栈上的容器类用于堆上的类的指针,它也将进行清理
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
之间的主要区别在于它们的语义w.r.t.所有权。您可以在Smart Pointers上的维基百科页面上查看概述。