我一直试图在Python中找到RAII.资源分配是初始化是C++中的一种模式,在该模式中,对象在创建时进行初始化.如果失败,则抛出异常.通过这种方式,程序员知道对象永远不会处于半构造状态.Python可以做到这一点.
但RAII也适用于C++的范围规则,以确保迅速破坏对象.一旦变量弹出堆栈就会被破坏.这可能发生在Python中,但仅限于没有外部或循环引用.
更重要的是,对象的名称仍然存在,直到它退出的函数(有时更长).模块级别的变量将在模块的使用寿命期间保持不变.
如果我这样做,我想得到一个错误:
for x in some_list:
...
... 100 lines later ...
for i in x:
# Oops! Forgot to define x first, but... where's my error?
...
Run Code Online (Sandbox Code Playgroud)
我可以在使用它之后手动删除这些名称,但这样会非常难看,而且我需要付出努力.
我希望在这种情况下做什么 - 我意味着什么:
for x in some_list:
surface = x.getSurface()
new_points = []
for x,y,z in surface.points:
... # Do something with the points
new_points.append( (x,y,z) )
surface.points = new_points
x.setSurface(surface)
Run Code Online (Sandbox Code Playgroud)
Python做了一些范围界定,但不是在缩进级别,只是在功能级别.要求我创建一个新函数来定义变量以便我可以重用一个名称似乎很愚蠢.
Python 2.5具有"with"语句,
但这需要我明确地放入__enter__和__exit__函数,并且通常似乎更倾向于清理文件和互斥锁等资源,而不管退出向量.它对范围界定没有帮助.或者我错过了什么?
我搜索过"Python RAII"和"Python范围",我无法直接和权威地找到解决问题的任何内容.我查看了所有的PEP.这个概念似乎没有在Python中得到解决.
我是一个坏人,因为我想在Python中使用范围变量?这是不太Pythonic?
我不是喜欢它吗?
也许我正试图剥夺语言动态方面的好处.有时希望范围得到执行是否自私?
我是否因为希望编译器/解释器能够捕获我的疏忽变量重用错误而懒惰?嗯,是的,当然我很懒,但我是不是很懒?
我熟悉RAII的优点,但我最近在这样的代码中遇到了问题:
class Foo
{
public:
Foo()
{
DoSomething();
...
}
~Foo()
{
UndoSomething();
}
}
Run Code Online (Sandbox Code Playgroud)
一切都很好,除了构造函数...部分中的代码抛出异常,结果UndoSomething()从未被调用.
有明显的方法来解决这个特定的问题,比如...在一个try/catch块然后调用UndoSomething(),但是a:那是重复的代码,而b:try/catch块是一种代码气味,我试图通过使用RAII技术来避免.而且,如果涉及多个Do/Undo对,代码可能会变得更糟,更容易出错,而且我们必须在中途进行清理.
我想知道有一个更好的方法来做到这一点 - 也许一个单独的对象需要一个函数指针,并在它反过来被破坏时调用该函数?
class Bar
{
FuncPtr f;
Bar() : f(NULL)
{
}
~Bar()
{
if (f != NULL)
f();
}
}
Run Code Online (Sandbox Code Playgroud)
我知道不会编译,但它应该显示原则.Foo然后变成......
class Foo
{
Bar b;
Foo()
{
DoSomething();
b.f = UndoSomething;
...
}
}
Run Code Online (Sandbox Code Playgroud)
请注意,foo现在不需要析构函数.这听起来比它的价值更麻烦吗,或者这已经是一个常见的模式,有助于我处理繁重的事情吗?
在C++缓冲区中读取整个文件内容的好方法是什么?
虽然在普通CI中可以使用fopen(), fseek(), fread()函数组合并将整个文件读取到缓冲区,但对C++使用它仍然是个好主意吗?如果是,那么我怎样才能在打开时使用RAII方法,为缓冲区分配内存,读取和读取文件内容到缓冲区.
我应该为缓冲区创建一些包装类,它在它的析构函数中释放内存(分配给缓冲区),以及用于文件处理的相同包装器吗?
为什么C++标准库流使用open()/ close()语义与对象生命周期分离?关闭破坏可能仍然在技术上使类RAII,但获取/释放独立性在范围中留下漏洞,其中句柄可以指向什么,但仍然需要运行时检查来捕获.
为什么图书馆设计师选择他们的方法而不是只在构造失败的构造函数中打开?
void foo() {
std::ofstream ofs;
ofs << "Can't do this!\n"; // XXX
ofs.open("foo.txt");
// Safe access requires explicit checking after open().
if (ofs) {
// Other calls still need checks but must be shielded by an initial one.
}
ofs.close();
ofs << "Whoops!\n"; // XXX
}
// This approach would seem better IMO:
void bar() {
std_raii::ofstream ofs("foo.txt"); // throw on failure and catch wherever
// do whatever, then close ofs on destruction ... …Run Code Online (Sandbox Code Playgroud) 我喜欢const成员变量的想法,特别是当我将C函数包装到类中时.构造函数获取在整个对象生命周期内保持有效的资源句柄(例如文件描述符),析构函数最终将其关闭.(那是RAII背后的想法,对吧?)
但是使用C++ 0x移动构造函数我遇到了问题.由于析构函数也在"卸载"对象上调用,我需要防止资源句柄的清理.由于成员变量是const,我无法分配值-1或INVALID_HANDLE(或等效值)来向析构函数指示它不应该执行任何操作.
有没有办法在对象的状态被移动到另一个对象时不调用析构函数?
例:
class File
{
public:
// Kind of "named constructor" or "static factory method"
static File open(const char *fileName, const char *modes)
{
FILE *handle = fopen(fileName, modes);
return File(handle);
}
private:
FILE * const handle;
public:
File(FILE *handle) : handle(handle)
{
}
~File()
{
fclose(handle);
}
File(File &&other) : handle(other.handle)
{
// The compiler should not call the destructor of the "other"
// object.
}
File(const File &other) = delete;
File &operator =(const File &other) …Run Code Online (Sandbox Code Playgroud) 我正在研究一段代码,它有许多可能的故障点,导致它提前退出函数.我正在与之交互的库要求将C样式的数组传递给函数.所以,我没有在每个出口点调用数组上的delete,而是这样做:
void SomeFunction(int arrayLength)
{
shared_ptr<char> raiiArray(new char[arrayLength]);
pArray = raiiArray.get();
if(SomeFunctionThatRequiresCArray(pArray) == FAILED) { return; }
//etc.
}
Run Code Online (Sandbox Code Playgroud)
我想使用unique_ptr,但我当前的编译器不支持它,并且引用计数开销在这种情况下并不重要.
我只是想知道在与遗留代码接口时是否有人对这种做法有任何想法.
更新 我完全忘了shared_ptr呼叫delete而不是delete [].我刚看到没有内存泄漏,并决定采用它.甚至没想过用矢量.因为我最近一直在钻研新的(对我来说)C++我认为我有一个案例"如果你拥有的唯一工具是锤子,那么一切看起来都像钉子一样." 综合征.感谢您的反馈.
UPDATE2我想我会改变这个问题并提供一个答案,让那些犯了同样错误的人更有价值.虽然有类似的替代方案scoped_array,shared_array并且vector,您可以使用a shared_ptr来管理数组的范围(但在此之后我不知道为什么我会想要):
template <typename T>
class ArrayDeleter
{
public:
void operator () (T* d) const
{
delete [] d;
}
};
void SomeFunction(int arrayLength)
{
shared_ptr<char> raiiArray(new char[arrayLength], ArrayDeleter<char>());
pArray = raiiArray.get();
if(SomeFunctionThatRequiresCArray(pArray) == FAILED) { return; }
//etc. …Run Code Online (Sandbox Code Playgroud) 我不能想到在规范中也有尾调用优化的真正的RAII语言,但我知道许多C++实现可以作为特定于实现的优化来实现.
这给那些那些实现了一个问题:因为析构函数,并在自动变量的作用域结束时被调用不是由一个单独的垃圾收集例程,是不是违反TCO的约束,递归调用必须在最后的指令功能结束?
例如:-
#include <iostream>
class test_object {
public:
test_object() { std::cout << "Constructing...\n"; }
~test_object() { std::cout << "Destructing...\n"; }
};
void test_function(int count);
int main()
{
test_function(999);
}
void test_function(int count)
{
if (!count) return;
test_object obj;
test_function(count - 1);
}
Run Code Online (Sandbox Code Playgroud)
"构建......"将写入999次,然后"破坏......"再写999次.最终,test_object在展开之前将自动分配999个实例.但假设一个实现有TCO,那么1000个堆栈帧是存在还是仅存在1?
递归调用之后的析构函数是否与事实上的TCO实现要求相冲突?
问题:如果我想要的只是显示错误消息并关闭(假设我可能在程序中很深),是否正确使用异常来终止我的程序?我可以只显式调用exit()这样的东西吗?
我目前在做什么:
我正在开发一个游戏项目,并试图找出在需要执行此类操作的错误的情况下终止程序的最佳方法.例如,在无法加载纹理的情况下,我显示错误消息并终止程序.
我目前正在这样做,例如:
int main()
{
Game game;
try
{
game.run();
}
catch (BadResolutionException & e)
{
Notification::showErrorMessage(e.what(), "ERROR: Resolution");
return 1;
}
catch (BadAssetException & e)
{
Notification::showErrorMessage(e.what(), "ERROR: Assets");
return 1;
}
catch (std::bad_alloc & e)
{
Notification::showErrorMessage(e.what(), "ERROR: Memory");
return 1;
}
return 0;
}
Run Code Online (Sandbox Code Playgroud)
除了bad_alloc之外的所有内容都是我自己定义的从runtime_error派生的异常.
我不需要任何手动资源清理,我使用std :: unique_ptr进行任何动态分配.我只需要显示错误消息并关闭程序.
研究/替代例外:
我在SO和其他地方查了很多帖子,并且看到其他人说不要使用例外,使用例外,但你使用它们是错误的.我也查找过显式调用exit()之类的东西.
使用exit()听起来不错,但是我看到它不会通过调用堆栈返回主要清理所有内容(如果我能再次找到这个,我会发布链接).此外,根据http://www.cplusplus.com/reference/cstdlib/exit/,如果多个线程处于活动状态,则不应使用此选项.我确实希望在短时间内创建第二个线程至少一次,并且该线程可能会发生错误.
这里有一些关于游戏的回复中提到了不使用例外的问题https://gamedev.stackexchange.com/questions/103285/how-industy-games-handle-their-code-errors-and-exceptions
这里讨论了使用例外情况:http://www.quora.com/Why-do-some-people-recommend-not-using-exception-handling-in-C++
我读过其他一些资料,但那些是我最近看过的资料.
个人结论:
由于我处理错误处理和使用异常的经验有限,我不确定我是否走在正确的轨道上.我根据上面发布的代码选择了使用异常的路线.如果您同意我应该解决这些例外情况,我是否正确使用它?
raii ×10
c++ ×8
c++11 ×2
exception ×1
iostream ×1
python ×1
recursion ×1
scope ×1
scopeguard ×1
shared-ptr ×1