C++ 除了C+之外,其他语言的程序员也会这样做+;,使用、了解或理解RAII?
我注意到RAII在Stackoverflow上得到了很多关注,但是在我的圈子里(主要是C++),RAII是如此明显,它就像在问什么是类或析构函数C++ 除了C+之外,其他语言的程序员也会这样做+;,使用、了解或理解RAII?,c++,language-agnostic,raii,C++,Language Agnostic,Raii,我注意到RAII在Stackoverflow上得到了很多关注,但是在我的圈子里(主要是C++),RAII是如此明显,它就像在问什么是类或析构函数 所以我很好奇,因为我每天都被硬核C++程序员包围着,RAII不仅仅是众所周知的(包括C++),或者如果StAcExoad上的所有问题都是因为我现在和那些没有用C++成长的程序员联系的话,在其他语言中,人们只是不使用/不知道RAII?这与知道何时调用析构函数有关,对吗?因此,这并不完全是语言不可知论,因为这在许多GC语言中都不是给定的。我认为许多其他语言
所以我很好奇,因为我每天都被硬核C++程序员包围着,RAII不仅仅是众所周知的(包括C++),或者如果StAcExoad上的所有问题都是因为我现在和那些没有用C++成长的程序员联系的话,在其他语言中,人们只是不使用/不知道RAII?这与知道何时调用析构函数有关,对吗?因此,这并不完全是语言不可知论,因为这在许多GC语言中都不是给定的。我认为许多其他语言(例如,没有
删除
的语言)并没有给予程序员对对象生命周期的完全相同的控制,因此必须有其他方法来提供对资源的确定性处理。在C语言中,例如使用<代码>使用<代码> >代码> IDsPiabase是常见的。 RAII在C++中很流行,因为它是少数几种(只能)分配复杂范围局部变量的语言之一,但没有<代码>最后< /Cord>子句。C#、Java、Python和Ruby都有
finally
或同等版本。C没有最终
,但当变量超出范围时也不能执行代码。对于在这篇文章中评论RAII(资源获取是初始化)的人,这里有一个激励性的例子
class StdioFile {
FILE* file_;
std::string mode_;
static FILE* fcheck(FILE* stream) {
if (!stream)
throw std::runtime_error("Cannot open file");
return stream;
}
FILE* fdup() const {
int dupfd(dup(fileno(file_)));
if (dupfd == -1)
throw std::runtime_error("Cannot dup file descriptor");
return fdopen(dupfd, mode_.c_str());
}
public:
StdioFile(char const* name, char const* mode)
: file_(fcheck(fopen(name, mode))), mode_(mode)
{
}
StdioFile(StdioFile const& rhs)
: file_(fcheck(rhs.fdup())), mode_(rhs.mode_)
{
}
~StdioFile()
{
fclose(file_);
}
StdioFile& operator=(StdioFile const& rhs) {
FILE* dupstr = fcheck(rhs.fdup());
if (fclose(file_) == EOF) {
fclose(dupstr); // XXX ignore failed close
throw std::runtime_error("Cannot close stream");
}
file_ = dupstr;
return *this;
}
int
read(std::vector<char>& buffer)
{
int result(fread(&buffer[0], 1, buffer.size(), file_));
if (ferror(file_))
throw std::runtime_error(strerror(errno));
return result;
}
int
write(std::vector<char> const& buffer)
{
int result(fwrite(&buffer[0], 1, buffer.size(), file_));
if (ferror(file_))
throw std::runtime_error(strerror(errno));
return result;
}
};
int
main(int argc, char** argv)
{
StdioFile file(argv[1], "r");
std::vector<char> buffer(1024);
while (int hasRead = file.read(buffer)) {
// process hasRead bytes, then shift them off the buffer
}
}
类标准文件{
文件*文件*;
std::字符串模式;
静态文件*fcheck(文件*stream){
如果(!流)
抛出std::runtime_错误(“无法打开文件”);
回流;
}
文件*fdup()常量{
int dupfd(dup(fileno(file_));
如果(dupfd==-1)
抛出std::runtime_错误(“无法复制文件描述符”);
返回fdopen(dupfd,mode_u.c_str());
}
公众:
标准文件(字符常量*名称,字符常量*模式)
:文件(fcheck(fopen(名称,模式)),模式(模式)
{
}
标准文件(标准文件常量和rhs)
:文件(fcheck(rhs.fdup())、模式(rhs.mode)
{
}
~StdioFile()
{
fclose(文件);
}
标准文件和运算符=(标准文件常量和rhs){
文件*dupstr=fcheck(rhs.fdup());
if(fclose(file_)==EOF){
fclose(dupstr);//XXX忽略关闭失败
抛出std::runtime_错误(“无法关闭流”);
}
文件=dupstr;
归还*这个;
}
int
读取(标准::向量和缓冲区)
{
int结果(fread(&buffer[0],1,buffer.size(),file));
如果(ferror(文件)
抛出std::运行时错误(strerror(errno));
返回结果;
}
int
写入(标准::向量常量和缓冲区)
{
int结果(fwrite(&buffer[0],1,buffer.size(),file));
如果(ferror(文件)
抛出std::运行时错误(strerror(errno));
返回结果;
}
};
int
主(内部argc,字符**argv)
{
StdioFile文件(argv[1],“r”);
std::向量缓冲器(1024);
while(int hasRead=file.read(缓冲区)){
//处理hasRead字节,然后将它们移出缓冲区
}
}
这里,当创建StdioFile
实例时,获取资源(在本例中为文件流);当它被销毁时,资源被释放。不需要try
或finally
块;如果读取导致异常,fclose
将自动调用,因为它位于析构函数中
当函数离开
main
时,无论是正常调用还是异常调用析构函数。在这种情况下,将清除文件流。世界又一次安全了-D<P>P>RAII是对C++的特异性。C++具有堆栈分配对象、非托管对象生命周期和异常处理的必要组合。 < P>首先,我很惊讶,它不是很有名!我完全认为RAII至少对C++程序员来说是显而易见的。
不过现在我想我能理解为什么人们会问这个问题了。我被包围了,我的自我肯定是,C++怪胎……
所以我的秘密。。我想那应该是,多年前我一直在读梅耶斯、萨特[EDIT:]和安德烈的作品,直到我刚刚摸索出来。这根本不是语言不可知论。这里的咒语是因为C++工作的方式。在C++中,直到构造函数完成后才构造对象。如果对象尚未成功构造,则不会调用析构函数
翻译成实用的语言,一个构造器应该确保它涵盖了它不能彻底完成其工作的情况。例如,如果在构造过程中发生异常,那么构造函数必须优雅地处理它,因为析构函数不会在那里提供帮助。这通常是通过覆盖构造函数中的异常或将此麻烦转发给其他对象来完成的。例如:
class OhMy {
public:
OhMy() { p_ = new int[42]; jump(); }
~OhMy() { delete[] p_; }
private:
int* p_;
void jump();
};
如果构造函数中的jump()
调用抛出错误,我们就有麻烦了,因为p\uu
会泄漏。我们可以这样解决这个问题:
class Few {
public:
Few() : v_(42) { jump(); }
~Few();
private:
std::vector<int> v_;
void jump();
};
from __future__ import with_statement # required for python version < 2.6
from contextlib import closing
import urllib
with closing(urllib.urlopen('http://www.python.org')) as page:
for line in page:
print line
class少数{
公众:
少数():v_u42{jump();}
~level();
私人:
std::向量v_;
空跳();
};
如果人们没有意识到这一点,那是因为以下两件事之一:
-
他们对C++不太熟悉。在这种情况下,他们应该在编写下一个类之前再次打开。具体来说,本书第三版的第14.4.1节讨论了这种技术
他们根本不懂C++。那很好。这个习语很像C++y。要么学习C++,要么忘记所有这些,继续你的生活。最好学习C++。李>
- RAII
它以构造函数和析构函数开头,但它不仅仅是构造函数和析构函数。
这一切都是关于在出现异常时安全地控制资源
void someFunc()
{
// Assuming Java Like syntax;
StdioFile file = new StdioFile("Plop","r");
try
{
// use file
}
finally
{
// close file.
file.close(); //
// Using the finaliser is not enough as we can not garantee when
// it will be called.
}
}
try {
BufferedReader file = new BufferedReader(new FileReader("infilename"));
// do something with file
}
finally {
file.close();
}
File.open("foo.txt") do | file |
# do something with file
end
(with-open-file (file "foo.txt")
;; do something with file
)
(with-input-from-file "foo.txt"
(lambda ()
;; do something
)
try
file = open("foo.txt")
# do something with file
finally:
file.close()
' WaitCursor.cls '
Private m_OldCursor As MousePointerConstants
Public Sub Class_Inititialize()
m_OldCursor = Screen.MousePointer
Screen.MousePointer = vbHourGlass
End Sub
Public Sub Class_Terminate()
Screen.MousePointer = m_OldCursor
End Sub
Public Sub MyButton_Click()
Dim WC As New WaitCursor
' … Time-consuming operation. '
End Sub
with open("foo.txt", "w") as f:
f.write("abc")
from contextlib import contextmanager
@contextmanager
def closing(thing):
try:
yield thing
finally:
thing.close()
from __future__ import with_statement # required for python version < 2.6
from contextlib import closing
import urllib
with closing(urllib.urlopen('http://www.python.org')) as page:
for line in page:
print line
T RAIIWrapper<T>(Func<DbConnection, T> f){
using (var db = new DbConnection()){
return f(db);
}
}
(with-open-file (stream "file.ext" :direction :input)
(do-something-with-stream stream))