以下代码给了我一些意外的行为:
ActionListener ban = new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
int fila = table.getSelectedRow();
String nombre = (String) modelo.getValueAt(fila, 0);
modelo.removeRow(fila);
try {
removeUser(nombre);
} catch (IOException ex) {
System.out.println(ex.getMessage());
}
}
};
btnBanear.addActionListener(ban);
...
public void removeUser(String nombre) throws IOException {
String lee = null;
String usuario = "";
CharSequence aux = nombre;
try {
registro = new File("Registro.txt");
tempFile = new File("Registro1.txt");
lector = new BufferedReader(new FileReader(registro));
fw = new FileWriter(tempFile);
writer = new BufferedWriter(fw);
} catch (FileNotFoundException ex) {
System.out.println(ex.getMessage());
} catch (IOException ex) {
System.out.println(ex.getMessage());
} catch (NullPointerException e) {
System.out.println(e.getMessage());
}
while ((lee = lector.readLine()) != null) {
System.out.println(aux);
if (lee.contains(nombre)) {
continue;
} else {
writer.write(lee);
}
}
lector.close();
fw.close();
writer.close();
registro.delete();
}
我希望IOLoader类逐行读取字符串。我得到以下结果,但未注释部分:
#include <map>
#include <iostream>
#include <string>
#include <sstream>
const std::string data1 =
"column1 column2\n"
"1 3\n"
"5 6\n"
"49 22\n";
const std::string data2 =
"column1 column2 column3\n"
"10 20 40\n"
"30 20 10\n";
class IOLoader
{
public:
// accept an istream and load the next line with member Next()
IOLoader(std::istream& t_stream) : stream_(t_stream)
{
for(int i = 0; i < 2; ++i) std::getline(stream_, line_);
};// get rid of the header
IOLoader(std::istream&& t_stream) : stream_(t_stream)
{
for(int i = 0; i < 2; ++i) std::getline(stream_, line_);
};// get rid of the header
void Next()
{
// load next line
if(!std::getline(stream_, line_))
line_ = "";
};
bool IsEnd()
{ return line_.empty(); };
std::istream& stream_;
std::string line_;
};
int main()
{
for(IOLoader data1_loader = IOLoader((std::stringstream(data1))); !data1_loader.IsEnd(); data1_loader.Next())
{
std::cout << data1_loader.line_ << "\n";
// weird result if the following part is uncommented
/*
IOLoader data2_loader = IOLoader(std::stringstream(data2));
std::cout << data2_loader.line_ << "\n";
data2_loader.Next();
std::cout << data2_loader.line_ << "\n";
*/
}
}
这是完全可以预期的。问题是当我用data2_loader取消注释零件时会发生什么。现在它给了我:
1 3
5 6
49 22
我不知道发生了什么。这是我最初的期望:
1 3
10 20 40
30 20 10
mn349 22
10 20 40
30 20 10
由于任何原因,如果我使用data2创建一个字符串流,则data1均无法正确读取。我用g ++ 4.9.2编译它。非常感谢您的帮助。
答案 0 :(得分:3)
编写IOLoader data1_loader = IOLoader((std::stringstream(data1)));
时,会将IOLoader::stream_
引用成员绑定到临时,因为该std::stringstream(data1)
在构造函数之后被销毁。您只剩下从悬空引用到已破坏对象的阅读,这是未定义的行为,因此绝对有可能发生任何事情。一个简单的解决方法是将两个stringstream
都声明为可以持续IOLoader
需要它们的变量,并删除您的IOLoader(std::istream&& t_stream)
构造函数,因为它实际上并没有移动{{1} },作为r值参考,通常是临时的。
t_stream
如果您需要std::stringstream ss1 {data1};
for(IOLoader data1_loader = IOLoader(ss1); !data1_loader.IsEnd(); data1_loader.Next()){
std::cout << data1_loader.line_ << "\n";
std::stringstream ss2 { data2 };
IOLoader data2_loader = IOLoader(ss2);
std::cout << data2_loader.line_ << "\n";
data2_loader.Next();
std::cout << data2_loader.line_ << "\n";
}
与不能像IOLoader
那样拥有所有权的流进行一般性的工作,那么坚持引用成员是有意义的。请注意,只要使用了std::cin
成员,被引用的流就必须存在。否则,如果仅使用stream_
,则最简单的方法是假定流拥有所有权并将std::stringstream
设置为值类型。例如,您可以IOLoader::stream_
通过r值引用传递到构造函数的流。
答案 1 :(得分:2)
传递右值引用并将其保留在周围已损坏,几乎可以肯定会导致未定义的行为(UB)。我指的是以下代码,该代码有助于但不直接导致UB:
IOLoader(std::istream&& t_stream) : stream_(t_stream)
{
for(int i = 0; i < 2; ++i) std::getline(stream_, line_);
};// get rid of the header
构造函数使以下行可以静默触发UB:
for(IOLoader data1_loader = IOLoader((std::stringstream(data1))); !data1_loader.IsEnd(); data1_loader.Next())
此行创建一个临时(右值)stringstream
对象。由于这是一个右值,因此它的引用会愉快地传递给接受右值引用的IOLoader
的构造函数。但是接受rvalue-reference的构造函数不会移动任何东西,而只是存储对临时stringstream
的引用。这与右值引用的正常用法相反,后者是移动对象。在循环主体开始时,临时stringstream
已被破坏,并且stream_
指的是被破坏的对象。在Next()
中或以任何其他方式使用此类引用是UB。
您可以通过创建一个名为stingsstream
的对象来修复此错误实例:
std::stringstream tmp_stream(data1);
for(IOLoader data1_loader = IOLoader(tmp_stream); !data1_loader.IsEnd(); data1_loader.Next())
这将修复实例,但不会解决核心问题。核心问题是存在误导性的&&
构造函数。 &&
构造函数有两种选择,要么将其全部删除,要么使其实际上移动stringstream
:
class IOLoader
{
...
IOLoader(std::stringstream&& t_stream) : saved_stream_(std::move(t_stream)), stream_(saved_stream_)
{
for(int i = 0; i < 2; ++i) std::getline(stream_, line_);
};// get rid of the header
...
std::stringstream saved_stream_;
std::istream& stream_;
std::string line_;
};
缺点是,在这种情况下,它将仅适用于stringstream
,而不适用于类似类型istringstream
。您可以使用模板使其更通用(额外的堆分配的运行时成本):
class IOLoader
{
public:
....
// enable_if avoids regular references, so that we neither prefer this ctor
// over the other ctor, nor try to move from a regular lvalue reference.
template <typename Stream, typename = typename std::enable_if<!std::is_reference<Stream>::value>::type>
IOLoader(Stream&& t_stream) : saved_stream_(std::make_unique<typename std::decay<Stream>::type>(std::move(t_stream))), stream_(*saved_stream_)
{
for(int i = 0; i < 2; ++i) std::getline(stream_, line_);
};
...
std::unique_ptr<std::istream> saved_stream_;
std::istream& stream_;
std::string line_;
};
在我看来,这对于一次性使用而言太复杂了,除非大量的代码将要使用它,否则我将使用右值引用抛弃构造函数,而不是对其进行修复。