返回 0 之前主要崩溃
Main crashes before returning 0
我正在用 C++ 编写程序,遇到了最奇怪的错误。我的程序在 main 到 return 0.
之前崩溃了
我不明白程序为什么会在完成后 return 归零之前崩溃。
这怎么可能?
主要有以下几点:
#include <iostream>
#include <iomanip>
#include <utility>
#include <ctime>
#include "Text.h"
#define TIME(start, end) double((end) - (start)) / CLOCKS_PER_SEC
int main(int argc, char* argv[]) {
argv[1] = "gutenberg_shakespeare.txt";
argc = 2;
if (argc == 1) {
std::cerr << argv[0] << ": missing file operand\n";
return 1;
}
else if (argc != 2) {
std::cerr << argv[0] << ": too many arguments\n";
return 2;
}
std::clock_t cs, ce;
std::cout << std::fixed << std::setprecision(3);
cs = std::clock();
w3::Text a;
ce = std::clock();
std::cout << "Constructor " << TIME(cs, ce) << " seconds";
std::cout << " - a.size = " << a.size() << std::endl;
cs = std::clock();
w3::Text b(argv[1]);
ce = std::clock();
std::cout << "Constructor " << TIME(cs, ce) << " seconds";
std::cout << " - b.size = " << b.size() << std::endl;
cs = std::clock();
a = b;
ce = std::clock();
std::cout << "Copy Assignment " << TIME(cs, ce) << " seconds";
std::cout << " - a.size = " << a.size() << std::endl;
cs = std::clock();
a = std::move(b);
ce = std::clock();
std::cout << "Move Assignment " << TIME(cs, ce) << " seconds";
std::cout << " - a.size = " << a.size() << std::endl;
cs = std::clock();
w3::Text c = a;
ce = std::clock();
std::cout << "Copy Constructor " << TIME(cs, ce) << " seconds";
std::cout << " - c.size = " << c.size() << std::endl;
cs = std::clock();
w3::Text d = std::move(a);
ce = std::clock();
std::cout << "Move Constructor " << TIME(cs, ce) << " seconds";
std::cout << " - d.size = " << d.size() << std::endl;
cs = std::clock();
ce = std::clock();
std::cout << "Destructor " << TIME(cs, ce) << " seconds\n";
std::cout << "DONE";
return 0;
}
文本cpp文件:
#include "Text.h"
#include <fstream>
#include <iostream>
using namespace w3;
Text::Text() {
}
//MOVE Copy constructor
Text::Text(Text&& movefrom){
std::cout << "MOE COPY CONSTRUCTOR" << std::endl;
filename = movefrom.filename;
entries = movefrom.entries;
data = movefrom.data;
movefrom.entries = 0;
movefrom.data = nullptr;
}
//move assig operator
Text&& Text::operator=(Text&& movefrom) {
std::cout << "move assig operator" << std::endl;
if (&movefrom != this) {
filename = movefrom.filename;
entries = movefrom.entries;
if (data != nullptr) {
delete[] data;
entries = 0;
}
movefrom.data = nullptr;
}
movefrom.entries = 0;
return std::move(*this);
}
//constructor
Text::Text(const std::string& mystring) {
std::cout << "Constructor" << std::endl;
int count = 0;
filename = mystring;
std::string buffer;
std::ifstream myfile(filename);
if (!myfile.is_open()) {
filename.clear();
}
if(myfile.is_open()) {
while (getline(myfile, buffer)) { //Will fail at end of file
//std::cout << buffer << std::endl;
count++;
}
std::cout << "File is read";
data = new std::string[count];
myfile.clear();//.................reset file state
myfile.seekg(0, myfile.beg);//....reset file position
int x = 0;
for (int i = 0; i < count; i++) {
getline(myfile, data[i]);
}
std::cout << std::endl << "File is copied" << std::endl;
entries = count;
myfile.close();
}
}
//default constructor
Text::~Text() {
if (data != nullptr) {
delete[] data;
entries = 0;
}
data = nullptr;
}
//copy constructor
Text::Text(const Text& copyfrom) {
data = nullptr; //The object is empty
*this = copyfrom;
}
const Text& Text::operator=(const Text& copyfrom) {
std::cout << "copy assign operator" << std::endl;
if (this != ©from) {
if (data != nullptr) {
delete[] data;
entries = 0;
}
filename = copyfrom.filename;
entries = copyfrom.entries;
if (copyfrom.data != nullptr) { //If the object is not empty
data = new std::string[entries];
for (int i = 0; i < entries; i++) {
data[i] = copyfrom.data[i];
}
}
std::cout << "Data is assigned" << std::endl;
}
return *this;
}
size_t Text::size() const {
return entries;
}
void Text::print() {
for (int i = 0; i < entries; i++) {
std::cout << data[i] << std::endl;
}
}
编辑 >>> 头文件
#ifndef TEXT_H
#define TEXT_H
#define FILE_LENGTH 10
#include <string>
#include <iostream>
namespace w3 {
class Text {
private:
std::string filename;
std::string * data = nullptr;
size_t entries;
public:
Text(Text&& movefrom);
Text&& operator=(Text&& movefrom);
Text();
Text(const std::string& mystring);
Text(const Text& copyfrom);
~Text();
const Text& operator=(const Text& copyfrom);
size_t size() const;
void print();
};
}
#endif
我不得不说,失败不是我所期待的。在此向 OP 表示敬意。我期望双重删除,因为两个对象指向同一个池。所以这不是 运行-of-the-mill Rule of Three violation;有点不一样。
快速浏览一下,复制逻辑很好(如果可以,请考虑对数据使用 std::vector
' 如果这样做,您可以丢掉大约 1/4 的代码。)并且最大的死点是不完整的默认构造函数。
w3::Text a;
将调用基本的默认构造函数
Text::Text() {
}
这不会将 data
初始化为 nullptr
。这会导致 const Text& Text::operator=(const Text& copyfrom)
中出现问题:
if (data != nullptr)
{
delete[] data;
entries = 0;
}
当 a=b;
a.data
从未设置时,不太可能是 nullptr
并且会尝试释放它不拥有的存储空间。如果程序设法一瘸一拐地通过这个,它可以,它的状态现在是无效的,Crom 只知道它什么时候会失败。
Text::Text(): entries(0), data(nullptr)
{
}
解决了。
还有...
复制构造函数泄漏
Text::Text(const Text& copyfrom)
{
data = nullptr; //The object is empty
*this = copyfrom;
}
你怎么知道数据是空的?没有测试,所以噗!如果那里有任何东西,现在是无法到达的。但正如 Paul McKenzie 在上面指出的那样,根据 Copy 构造函数编写赋值运算符几乎总是更好。
移动赋值运算符有点不稳定。
Text&& Text::operator=(Text&& movefrom)
应该是
Text& Text::operator=(Text&& movefrom)
这意味着您不必用 return std::move(*this);
清除 this
。刚刚 return *this;
在析构函数中没有指向
data = nullptr;
该对象在该行之后立即销毁,因此置空 data
是浪费精力。
我正在用 C++ 编写程序,遇到了最奇怪的错误。我的程序在 main 到 return 0.
之前崩溃了我不明白程序为什么会在完成后 return 归零之前崩溃。
这怎么可能?
主要有以下几点:
#include <iostream>
#include <iomanip>
#include <utility>
#include <ctime>
#include "Text.h"
#define TIME(start, end) double((end) - (start)) / CLOCKS_PER_SEC
int main(int argc, char* argv[]) {
argv[1] = "gutenberg_shakespeare.txt";
argc = 2;
if (argc == 1) {
std::cerr << argv[0] << ": missing file operand\n";
return 1;
}
else if (argc != 2) {
std::cerr << argv[0] << ": too many arguments\n";
return 2;
}
std::clock_t cs, ce;
std::cout << std::fixed << std::setprecision(3);
cs = std::clock();
w3::Text a;
ce = std::clock();
std::cout << "Constructor " << TIME(cs, ce) << " seconds";
std::cout << " - a.size = " << a.size() << std::endl;
cs = std::clock();
w3::Text b(argv[1]);
ce = std::clock();
std::cout << "Constructor " << TIME(cs, ce) << " seconds";
std::cout << " - b.size = " << b.size() << std::endl;
cs = std::clock();
a = b;
ce = std::clock();
std::cout << "Copy Assignment " << TIME(cs, ce) << " seconds";
std::cout << " - a.size = " << a.size() << std::endl;
cs = std::clock();
a = std::move(b);
ce = std::clock();
std::cout << "Move Assignment " << TIME(cs, ce) << " seconds";
std::cout << " - a.size = " << a.size() << std::endl;
cs = std::clock();
w3::Text c = a;
ce = std::clock();
std::cout << "Copy Constructor " << TIME(cs, ce) << " seconds";
std::cout << " - c.size = " << c.size() << std::endl;
cs = std::clock();
w3::Text d = std::move(a);
ce = std::clock();
std::cout << "Move Constructor " << TIME(cs, ce) << " seconds";
std::cout << " - d.size = " << d.size() << std::endl;
cs = std::clock();
ce = std::clock();
std::cout << "Destructor " << TIME(cs, ce) << " seconds\n";
std::cout << "DONE";
return 0;
}
文本cpp文件:
#include "Text.h"
#include <fstream>
#include <iostream>
using namespace w3;
Text::Text() {
}
//MOVE Copy constructor
Text::Text(Text&& movefrom){
std::cout << "MOE COPY CONSTRUCTOR" << std::endl;
filename = movefrom.filename;
entries = movefrom.entries;
data = movefrom.data;
movefrom.entries = 0;
movefrom.data = nullptr;
}
//move assig operator
Text&& Text::operator=(Text&& movefrom) {
std::cout << "move assig operator" << std::endl;
if (&movefrom != this) {
filename = movefrom.filename;
entries = movefrom.entries;
if (data != nullptr) {
delete[] data;
entries = 0;
}
movefrom.data = nullptr;
}
movefrom.entries = 0;
return std::move(*this);
}
//constructor
Text::Text(const std::string& mystring) {
std::cout << "Constructor" << std::endl;
int count = 0;
filename = mystring;
std::string buffer;
std::ifstream myfile(filename);
if (!myfile.is_open()) {
filename.clear();
}
if(myfile.is_open()) {
while (getline(myfile, buffer)) { //Will fail at end of file
//std::cout << buffer << std::endl;
count++;
}
std::cout << "File is read";
data = new std::string[count];
myfile.clear();//.................reset file state
myfile.seekg(0, myfile.beg);//....reset file position
int x = 0;
for (int i = 0; i < count; i++) {
getline(myfile, data[i]);
}
std::cout << std::endl << "File is copied" << std::endl;
entries = count;
myfile.close();
}
}
//default constructor
Text::~Text() {
if (data != nullptr) {
delete[] data;
entries = 0;
}
data = nullptr;
}
//copy constructor
Text::Text(const Text& copyfrom) {
data = nullptr; //The object is empty
*this = copyfrom;
}
const Text& Text::operator=(const Text& copyfrom) {
std::cout << "copy assign operator" << std::endl;
if (this != ©from) {
if (data != nullptr) {
delete[] data;
entries = 0;
}
filename = copyfrom.filename;
entries = copyfrom.entries;
if (copyfrom.data != nullptr) { //If the object is not empty
data = new std::string[entries];
for (int i = 0; i < entries; i++) {
data[i] = copyfrom.data[i];
}
}
std::cout << "Data is assigned" << std::endl;
}
return *this;
}
size_t Text::size() const {
return entries;
}
void Text::print() {
for (int i = 0; i < entries; i++) {
std::cout << data[i] << std::endl;
}
}
编辑 >>> 头文件
#ifndef TEXT_H
#define TEXT_H
#define FILE_LENGTH 10
#include <string>
#include <iostream>
namespace w3 {
class Text {
private:
std::string filename;
std::string * data = nullptr;
size_t entries;
public:
Text(Text&& movefrom);
Text&& operator=(Text&& movefrom);
Text();
Text(const std::string& mystring);
Text(const Text& copyfrom);
~Text();
const Text& operator=(const Text& copyfrom);
size_t size() const;
void print();
};
}
#endif
我不得不说,失败不是我所期待的。在此向 OP 表示敬意。我期望双重删除,因为两个对象指向同一个池。所以这不是 运行-of-the-mill Rule of Three violation;有点不一样。
快速浏览一下,复制逻辑很好(如果可以,请考虑对数据使用 std::vector
' 如果这样做,您可以丢掉大约 1/4 的代码。)并且最大的死点是不完整的默认构造函数。
w3::Text a;
将调用基本的默认构造函数
Text::Text() {
}
这不会将 data
初始化为 nullptr
。这会导致 const Text& Text::operator=(const Text& copyfrom)
中出现问题:
if (data != nullptr)
{
delete[] data;
entries = 0;
}
当 a=b;
a.data
从未设置时,不太可能是 nullptr
并且会尝试释放它不拥有的存储空间。如果程序设法一瘸一拐地通过这个,它可以,它的状态现在是无效的,Crom 只知道它什么时候会失败。
Text::Text(): entries(0), data(nullptr)
{
}
解决了。
还有...
复制构造函数泄漏
Text::Text(const Text& copyfrom)
{
data = nullptr; //The object is empty
*this = copyfrom;
}
你怎么知道数据是空的?没有测试,所以噗!如果那里有任何东西,现在是无法到达的。但正如 Paul McKenzie 在上面指出的那样,根据 Copy 构造函数编写赋值运算符几乎总是更好。
移动赋值运算符有点不稳定。
Text&& Text::operator=(Text&& movefrom)
应该是
Text& Text::operator=(Text&& movefrom)
这意味着您不必用 return std::move(*this);
清除 this
。刚刚 return *this;
在析构函数中没有指向
data = nullptr;
该对象在该行之后立即销毁,因此置空 data
是浪费精力。