【问题标题】:Main crashes before returning 0 [closed]返回0之前主要崩溃[关闭]
【发布时间】:2016-07-08 14:30:36
【问题描述】:

我正在用 C++ 编写程序,遇到了最奇怪的错误。我的程序在 main 返回 0 之前就崩溃了。

我不明白程序如何在完成后但在返回零之前崩溃。

这怎么可能?

主要有以下几点:

#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 != &copyfrom) {

        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

【问题讨论】:

  • 任何错误信息?
  • 我敢打赌这不是一个小例子。
  • w3::Text a; 可能有问题?
  • 一旦我删除了所有无法编译的东西,我就无法重现。可能是我必须删除的东西中的一个错误。您确定w3::Text 符合三规则吗?如果不是,那么a = b; 会给你带来麻烦。什么是三法则? Glad you asked!
  • 这应该是完整的 -- 不是。 -- 而且完全不可能 -- 这确实是可能的。 ideone.com/2FSrPm 我刚刚添加了一个错误的w3::Text 课程,因为你没有发布它。如前一条评论所述,我上的课程违反了 3 规则。

标签: c++ crash main argv argc


【解决方案1】:

我不得不说,失败不是我所期待的。对 OP 表示敬意。我期望双重删除,因为两个对象指向同一个池。所以这不是普通的Rule of Three violation;有点不一样。

快速浏览说复制逻辑很好(如果可以,请考虑使用std::vector 获取数据'如果这样做,您可以丢弃大约 1/4 的代码。)并且最大的死点是不完整的默认构造函数。

w3::Text a;

将调用准系统默认构造函数

Text::Text() {

} 

不会将data 初始化为nullptr。这会导致const Text&amp; Text::operator=(const Text&amp; 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 归零是浪费精力。

【讨论】:

  • 我越来越习惯 Java,这使得使用 c++ 变得更加困难。事情经常崩溃,却没有说明原因。
  • @bigcodeszzer 远离原始指针,开始使用容器类和智能指针。然后 C++ 可以变得更易于使用,几乎和 Java 一样简单,因为您不必编写所有这些代码来维护一个类。如果你这样做了,你很可能不会遇到你遇到的问题。将 std::string* 成员替换为 std::vector&lt;std::string&gt; 将是使用容器类而不是原始指针的主要示例。
  • 这是有道理的。不过我在学校,他们希望我们首先知道如何使用所有的基本知识。我很兴奋我可以使用字符串而不是 char *。
  • 我不得不说我很喜欢 Java 为您提供详细堆栈跟踪的能力。而且我想念我在过去的美好时光中获得的详细堆栈转储。啊啊啊Windows NT4。微软,你怎么错了?无论如何,当你在构造函数之后有成员未初始化时,一些 IDE 会警告你,there's this great tool called Valgrind 用于跟踪初始化和内存踩踏错误。
  • 是的,Java 非常棒。我也喜欢 valgrind,但我只使用过 linux 版本,如果有一个内置的 IDE 会很酷。我只使用 Visual Studio。我一直在尝试使用 try/throw catch 来跟踪事物并从中继承所有内容的基类的想法。
猜你喜欢
  • 1970-01-01
  • 2021-08-25
  • 2023-03-22
  • 2021-10-03
  • 1970-01-01
  • 1970-01-01
  • 2016-04-14
  • 1970-01-01
  • 2013-08-08
相关资源
最近更新 更多