为什么我的类 std::vector 成员总是抛出段错误?

Why does my class std::vector member always throw a segfault?

本文关键字:段错误 错误 我的 std vector 成员 为什么      更新时间:2023-10-16

我在SE上无休止地寻找为什么会发生这种情况的合乎逻辑的解释。 这可能是我忽略的非常简单的事情,但是我无法发现它,并且非常感谢对此的一些帮助。

上周,我实现了一个类来从.ini文件中读取系统调用的输出,然后查找所需信息并将其存储到自定义对象中,然后存储在 Config 类内的向量中。 它是一个单例配置类,为创建的自定义类的每个实例存储unique_ptr。

问题是,当我上周在我的笔记本电脑上实现这一点时,我对成员向量的读写问题为零,并且能够让它完全按照我的需要工作。 自从拉到我的台式计算机以来,这个向量以及我用作类成员的任何 STL 容器在我尝试在其上执行任何操作(甚至获取其大小(时都会引发分段错误。

我试图缩短下面的代码,只包含实际使用此向量的部分。 我已经用 A 替换了我的配置,用 T 替换了自定义类,无论我在哪里尝试使用我的成员容器,或者我添加到类的任何其他测试 STL 容器,我都会得到一个段错误。

作为记录,我将Qt与C++11一起使用。

更新:此示例在调试时在 c.cpp 的第 50 行以及尝试调用向量的任何位置中断。

调试在 stl_vector.h 中指向此行

// [23.2.4.2] capacity
/**  Returns the number of elements in the %vector.  */
size_type
size() const _GLIBCXX_NOEXCEPT
/*-> this line */ { return size_type(this->_M_impl._M_finish - this->_M_impl._M_start); }

主.cpp

#include "c.h"
int main(int argc, char *argv[])
{
C *c = C::getInstance();
delete c;
return 0;
}

t.h - 类存储文件中的信息

#include <string>
class T
{
public:
T();
bool Active();
std::string getA();
void setA(std::string);
private:
std::string a;
};

t.cpp

#include "t.h"
T::T()
{
}
bool T::Active()
{
if(a == "")
{
return false;
}
return true;
}
std::string T::getA()
{
return this->a;
}
void T::setA(std::string newa)
{
this->a = newa;
}

c.h - 类存储 T 对象并解析文件以获取信息

#include "t.h"
#include <QDebug>
#include <vector>
#include <algorithm>
#include <iostream>
#include <memory>
#include <sstream>
#include <fstream>
class C
{
public:
static C* getInstance();
private:
C();
static C* instance;
static bool init;
std::vector<std::unique_ptr<T>> t_list;
void readLines(const std::string&);
};

c.cpp

#include "c.h"
bool C::init = false;
C* C::instance = nullptr;
C::C()
{
system("echo this is a test command > a.ini");
instance->readLines("a.ini");
}
C* C::getInstance()
{
if(!init)
{
instance = new C;
init = true;
}
return instance;
}
void C::readLines(const std::string &path)
{
T* new_t;
std::ifstream file(path.c_str());
if(!file.is_open())
{
qDebug() << "Unable to open " << path.c_str();
}
std::ofstream o("test.txt");
std::string line;
while(std::getline(file, line))
{
// Split string before searching
std::stringstream ss(line);
std::string seg;
std::vector<std::string> split;
std::string left, right;
// Search patterns
size_t find_a = line.find("a");
size_t del = line.find(':');
if(find_a != std::string::npos)
{
o << "test_Size: " << t_list.size() << std::endl;

if(new_t->Active())
{
T* temp = new_t;
std::unique_ptr<T> move_t(temp);
t_list.push_back(std::move(move_t));
}
o << "test: " << t_list.size() << std::endl;
std::string n;
// Check if previous ahas any null elements
// Split string to find a
n = line.substr(line.find("a "));
n = n.substr(n.find(" ", +2));
new_t->setA(n);
}
else
{
continue;
}
}
// Add last a
T* t = new_t;
std::unique_ptr<T> move_t(t);
//t_list.push_back(std::move(move_t));
o << "a: " << t_list.back().get()->getA() << std::endl;
o << t_list.size() << std::endl;
o.close();
file.close();
}

代码更改后更新:

我现在看到两件事:一是C::readlines中的new_t从未初始化过,因此当稍后在函数中调用new_t->Active()时,这可能会中断。但是,我相信您遇到的主要问题是在C::C(),它说

instance->readLines("a.ini");

在执行的这一点上,C::instance尚未初始化 - 您只是在构造稍后分配给它的对象。因此,readlines调用中的this无效,任何访问对象成员的尝试都将导致 UB。后一个问题可以通过调用来解决

readLines("a.ini");

在这种情况下,当前构造的对象(稍后将被instance(用于this。不过,我不知道你想在第一次发生什么,所以我只能说:如果你想有一个vector<unique_ptr<T>>,你必须创建具有new T()或(可以说是(std::make_unique<T>()T类型的对象并将它们放在那里。

我还要说,这是在C++中实现单例的一种相当丑陋的方式。我的意思是,单例从来都不是很漂亮,但是如果你打算在C++中做到这一点,通常的方式就像C++单例设计模式的公认答案一样。

旧答案:

问题(如果是唯一的问题,我无法验证,因为您没有提供 MCVE(在行中

T move_t = new_T;
std::unique_ptr<Adapter> ptr_t(&move_t); // <-- particularly this one
m_ts.push_back(std::move(ptr_t));

您正在将指向本地对象的指针传递到std::unique_ptr中,但std::unique_ptr的全部目的是处理分配了new的对象以避免内存泄漏。一旦离开此声明周围的范围,您传入的指针不仅无效,即使不是这种情况,unique_ptr也会尝试在其生命周期结束时delete不在堆上的对象。这两个问题都会导致未定义的行为。

对我来说,看起来你真的想使用std::vector<T>而不是std::vector<std::unique_ptr<T>>,但这是一个你必须自己回答的设计问题。

在这里回答我自己的问题。 我正在尝试从保存它的对象的构造函数中调用一个成员变量,因此我尝试访问的向量尚未实例化,并且不存在内存中。 这就是导致分段错误发生的原因,我正在尝试访问尚未分配的内存,因此作用于我的 C 类的任何成员的任何调用都会导致此问题。

我通过向类添加一个公共函数来解决此问题,该函数随后调用私有readLines()函数。 我从将获得它所有权的对象调用该公共函数,并且由于这发生在实例化之后,因此内存是可访问的,问题消失了。