在引用超出范围后如何检测 ptr 是否仍在引用有效引用
How to detect if a ptr is still referencing a valid reference after that reference goes out of scope
我正在研究流,但无法理解以下内容。
这里我们有一个基本的 ostream ptr,它被设置为不同的输出流,无论是 cout
、cerr
还是 file
。
// ostream ptr
std::ostream* outstream;
// set output ostream
void setOutput(std::ostream & os)
{
outstream = &os;
}
// write message to ostream
void writeData(const std::string & msg)
{
*outstream << msg << '\n';
}
int main (int argc, char * const argv[])
{
// init to std out
setOutput(std::cout);
writeData("message to cout");
setOutput(std::cerr);
writeData("message to cerr");
std::ofstream fileout("test.txt", std::ofstream::out | std::ofstream::app);
setOutput(fileout);
writeData("message to file");
//fileout.close();
setOutput(std::cout);
writeData("message2 to cout");
return 0;
}
上面的代码运行完美,展示了 c++ iostream 实现的强大功能。完美的。
但是,由于 setOutput
是通过引用设置的,因此引用的对象必须保留在范围内。这就是问题出现的地方。如果 ofstream 或任何其他 ostream 无效,我想找出一种将输出默认为 std::cout
的方法。也就是说,引用的对象超出或超出范围。
例如:
// write message to ostream
void writeData(const std::string & msg)
{
if (/*stream or memory is invalid*/)
setOutput(std::cout);
*outstream << msg << '\n';
}
// local fileout goes out of scope
void foo()
{
std::ofstream fileout("test.txt", std::ofstream::out | std::ofstream::app);
setOutput(fileout);
writeData("message to file");
}
int main (int argc, char * const argv[])
{
setOutput(std::cout);
writeData("message to cout");
foo();
/* problem the local fileout is no longer referenced by the ostream ptr*/
/* the following should be redirected to std::cout cuz of default*/
writeData("message2 to cout");
return 0;
}
以上都很好,直到 foo()
returns 到 main 函数。那里出现了可怕的错误,因为本地定义的 ofstream
不再可访问。
显然这是不可取的,用户应该意识到这一点。但是,我想将所有这些都包装在日志记录 class 中,从而使对象的状态保持有效,即使这种滥用可能会发生。它将导致难以发现的无效访问冲突。
具体问题。有什么方法可以确定 ostream ptr 或与此相关的任何 ptr 是否仍在引用有效的对象或内存位置?
ps:我可以使用堆内存并用智能指针做一些事情,但坦率地说,如果可能的话我想保持这样
一种可能的方法是创建一个 RAII class 在将流传递到 setOutput 之前包装流。此 class 应设计为像 shared_ptr 一样工作,以便它维护共享引用计数。 writeData 然后检查它是否具有唯一剩余的引用,如果是,则销毁 ostream 并默认为 cout。
这听起来像是 RAII 的一个很好的用例。
写一个 class ,它接受一个文件名和一个 std::ostream**
作为其构造函数的参数。在上述class的构造函数中,构造ofstream(作为成员),并设置指向ofstream的指针。在析构函数中,还原为 stdout
.
然后,将以下函数的前两行替换为新的 class.
的声明
void foo()
{
std::ofstream fileout("test.txt", std::ofstream::out | std::ofstream::app);
setOutput(fileout);
writeData("message to file");
}
Concrete question. Is there any way to figure out whether an ostream ptr or any ptr for that matter is still referencing a valid object or memory location?
没有。没有办法用原始指针来解决这个问题。至少在标准 C++ 中不是。
您将需要保证所指向的对象在被指向时一直处于活动状态。
用于提供该保证的常见模式是 RAII,如其他答案中所述。保证指针有效性的另一种方法是使用智能指针而不是原始指针。但是,这些与自动变量不兼容。
只要你能保证指针不被取消引用,就可以继续指向死对象。这通常很难保证,因为如前所述,没有办法测试指向的对象是否存在。
您应该使用 RAII 强制正确设置流,然后在对象被销毁时设置回 std::cout。
class OutputStream
{
protected:
static std::ostream*& internalGlobalStateOfOutputStream()
{
static std::ostream* out = &std::cout;
return out;
}
public:
static std::ostream& getOutputStream()
{
return *internalGlobalStateOfOutputStream();
}
};
template<typename T>
class OutputStreamOwner: public OutputStream
{
T ownedStream;
public:
OutputStreamOwner(T&& obj)
: ownedStream(std::move(obj))
{
internalGlobalStateOfOutputStream() = &ownedStream;
}
template<typename... Args>
OutputStreamOwner(Args... args)
: ownedStream(args...)
{
internalGlobalStateOfOutputStream() = &ownedStream;
}
~OutputStreamOwner()
{
internalGlobalStateOfOutputStream() = & std::cout;
}
// Delete copy
OutputStreamOwner(OutputStreamOwner const&) = delete;
OutputStreamOwner& operator(OutputStreamOwner const&) = delete;
};
用法是:
void foo()
{
OutputStreamOwner<std::ofstream> output("test.txt", std::ofstream::out | std::ofstream::app);
writeData("message to file");
}
Concrete question. Is there any way to figure out whether an ostream
ptr or any ptr for that matter is still referencing a valid object or
memory location?
ps: I could use heap memory and do something with smart pointers but
frankly I'd want to keep it like this if possible
不,没有标准的方法来测试原始指针或引用是否仍在引用有效对象。
RAII 是针对此类问题的标准 C++ 解决方案,因此我认为您应该关注智能指针。我不知道有任何库提供的智能指针可以解决这个特定问题,但基于共享所有权的 RAII 解决方案似乎是这里最好的解决方案。
您可以使用将流作为输入的函数来避免这些复杂情况。
void writeData(std::ostream& os, const std::string & msg)
{
os << msg << '\n';
}
您可以通过返回流来进一步完善它,以允许对它进行链式调用:
std::ostream& os writeLine(std::ostream& os, const std::string & msg)
{
os << msg << '\n';
return os;
}
// declare stream
stream << writeLine(stream, "Foo") << writeLine(stream, "Bar");
事实上,这个功能更好也更容易维护,因为您不必记住在任何给定时间设置了哪个流。对于大型程序来说,这是一个重要的品质。
我正在研究流,但无法理解以下内容。
这里我们有一个基本的 ostream ptr,它被设置为不同的输出流,无论是 cout
、cerr
还是 file
。
// ostream ptr
std::ostream* outstream;
// set output ostream
void setOutput(std::ostream & os)
{
outstream = &os;
}
// write message to ostream
void writeData(const std::string & msg)
{
*outstream << msg << '\n';
}
int main (int argc, char * const argv[])
{
// init to std out
setOutput(std::cout);
writeData("message to cout");
setOutput(std::cerr);
writeData("message to cerr");
std::ofstream fileout("test.txt", std::ofstream::out | std::ofstream::app);
setOutput(fileout);
writeData("message to file");
//fileout.close();
setOutput(std::cout);
writeData("message2 to cout");
return 0;
}
上面的代码运行完美,展示了 c++ iostream 实现的强大功能。完美的。
但是,由于 setOutput
是通过引用设置的,因此引用的对象必须保留在范围内。这就是问题出现的地方。如果 ofstream 或任何其他 ostream 无效,我想找出一种将输出默认为 std::cout
的方法。也就是说,引用的对象超出或超出范围。
例如:
// write message to ostream
void writeData(const std::string & msg)
{
if (/*stream or memory is invalid*/)
setOutput(std::cout);
*outstream << msg << '\n';
}
// local fileout goes out of scope
void foo()
{
std::ofstream fileout("test.txt", std::ofstream::out | std::ofstream::app);
setOutput(fileout);
writeData("message to file");
}
int main (int argc, char * const argv[])
{
setOutput(std::cout);
writeData("message to cout");
foo();
/* problem the local fileout is no longer referenced by the ostream ptr*/
/* the following should be redirected to std::cout cuz of default*/
writeData("message2 to cout");
return 0;
}
以上都很好,直到 foo()
returns 到 main 函数。那里出现了可怕的错误,因为本地定义的 ofstream
不再可访问。
显然这是不可取的,用户应该意识到这一点。但是,我想将所有这些都包装在日志记录 class 中,从而使对象的状态保持有效,即使这种滥用可能会发生。它将导致难以发现的无效访问冲突。
具体问题。有什么方法可以确定 ostream ptr 或与此相关的任何 ptr 是否仍在引用有效的对象或内存位置?
ps:我可以使用堆内存并用智能指针做一些事情,但坦率地说,如果可能的话我想保持这样
一种可能的方法是创建一个 RAII class 在将流传递到 setOutput 之前包装流。此 class 应设计为像 shared_ptr 一样工作,以便它维护共享引用计数。 writeData 然后检查它是否具有唯一剩余的引用,如果是,则销毁 ostream 并默认为 cout。
这听起来像是 RAII 的一个很好的用例。
写一个 class ,它接受一个文件名和一个 std::ostream**
作为其构造函数的参数。在上述class的构造函数中,构造ofstream(作为成员),并设置指向ofstream的指针。在析构函数中,还原为 stdout
.
然后,将以下函数的前两行替换为新的 class.
的声明void foo()
{
std::ofstream fileout("test.txt", std::ofstream::out | std::ofstream::app);
setOutput(fileout);
writeData("message to file");
}
Concrete question. Is there any way to figure out whether an ostream ptr or any ptr for that matter is still referencing a valid object or memory location?
没有。没有办法用原始指针来解决这个问题。至少在标准 C++ 中不是。
您将需要保证所指向的对象在被指向时一直处于活动状态。
用于提供该保证的常见模式是 RAII,如其他答案中所述。保证指针有效性的另一种方法是使用智能指针而不是原始指针。但是,这些与自动变量不兼容。
只要你能保证指针不被取消引用,就可以继续指向死对象。这通常很难保证,因为如前所述,没有办法测试指向的对象是否存在。
您应该使用 RAII 强制正确设置流,然后在对象被销毁时设置回 std::cout。
class OutputStream
{
protected:
static std::ostream*& internalGlobalStateOfOutputStream()
{
static std::ostream* out = &std::cout;
return out;
}
public:
static std::ostream& getOutputStream()
{
return *internalGlobalStateOfOutputStream();
}
};
template<typename T>
class OutputStreamOwner: public OutputStream
{
T ownedStream;
public:
OutputStreamOwner(T&& obj)
: ownedStream(std::move(obj))
{
internalGlobalStateOfOutputStream() = &ownedStream;
}
template<typename... Args>
OutputStreamOwner(Args... args)
: ownedStream(args...)
{
internalGlobalStateOfOutputStream() = &ownedStream;
}
~OutputStreamOwner()
{
internalGlobalStateOfOutputStream() = & std::cout;
}
// Delete copy
OutputStreamOwner(OutputStreamOwner const&) = delete;
OutputStreamOwner& operator(OutputStreamOwner const&) = delete;
};
用法是:
void foo()
{
OutputStreamOwner<std::ofstream> output("test.txt", std::ofstream::out | std::ofstream::app);
writeData("message to file");
}
Concrete question. Is there any way to figure out whether an ostream ptr or any ptr for that matter is still referencing a valid object or memory location?
ps: I could use heap memory and do something with smart pointers but frankly I'd want to keep it like this if possible
不,没有标准的方法来测试原始指针或引用是否仍在引用有效对象。
RAII 是针对此类问题的标准 C++ 解决方案,因此我认为您应该关注智能指针。我不知道有任何库提供的智能指针可以解决这个特定问题,但基于共享所有权的 RAII 解决方案似乎是这里最好的解决方案。
您可以使用将流作为输入的函数来避免这些复杂情况。
void writeData(std::ostream& os, const std::string & msg)
{
os << msg << '\n';
}
您可以通过返回流来进一步完善它,以允许对它进行链式调用:
std::ostream& os writeLine(std::ostream& os, const std::string & msg)
{
os << msg << '\n';
return os;
}
// declare stream
stream << writeLine(stream, "Foo") << writeLine(stream, "Bar");
事实上,这个功能更好也更容易维护,因为您不必记住在任何给定时间设置了哪个流。对于大型程序来说,这是一个重要的品质。