将元素分配给映射值时 C++ 访问错误
C++ Bad access when assigning an element to map value
所以问题说明了问题...
背景:
我正在尝试从 HackerRank 解决 this problem。
它基本上是一个 html 标签解析器。保证有效输入,属性仅为字符串。
我的方法
我创建了一个自定义 Tag
class,它可以存储其他 Tag
的 map<string,Tag>
,以及 map<string,string>
的属性。解析似乎工作正常。
问题
在查询部分,我在以下 query/html 组合中得到 BAD_ACCESS
错误:
4 1
<a value = "GoodVal">
<b value = "BadVal" size = "10">
</b>
</a>
a.b~size
当我尝试从 a
访问 b
标签时发生错误。具体在t=t.tags[tag_name]
下面第118行
代码
#include <cmath>
#include <cstdio>
#include <vector>
#include <iostream>
#include <algorithm>
#include <sstream>
#include <map>
#include <stack>
using namespace std;
class Tag {
public:
Tag(){};
Tag(string name):name(name){};
string name;
map<string,Tag> tags = map<string, Tag>();
map<string,string> attribs=map<string,string>();
};
int main() {
int lines, queries;
std::cin>>lines>>queries;
std:string str;
getline(cin, str);
stack<string> open;
auto tags = map<string, Tag>();
for (int i = 0; i < lines; i++) {
getline(cin, str);
if (str.length()>1){
// If it's not </tag>, then it's an opening tag
if (str[1] != '/') {
// Parse tag name
auto wordidx = str.find(" ");
if (wordidx == -1) {
wordidx = str.length()-1.f;
}
string name = str.substr(1,wordidx-1);
auto t = Tag(name);
string sub = str.substr(wordidx);
auto equalidx=sub.find("=");
// Parse Attributes
while (equalidx != std::string::npos) {
string key = sub.substr(1,equalidx-2);
sub = sub.substr(equalidx);
auto attrib_start = sub.find("\"");
sub = sub.substr(attrib_start+1);
auto attrib_end = sub.find("\"");
string val = sub.substr(0, attrib_end);
sub = sub.substr(attrib_end+1);
t.attribs[key] = val;
equalidx=sub.find("=");
}
// If we're in a tag, push to that, else push to the base tags
if (open.size() == 0) {
tags[name] = t;
} else {
tags[open.top()].tags[name]=t;
}
open.push(name);
} else {
// Pop the stack if we reached a closing tag
auto wordidx = str.find(">");
string name = str.substr(2,wordidx-2);
// Sanity check, but we're assuming valid input
if (name.compare(open.top())) {
cout<<"FUCK"<<name<<open.top()<<endl;
return 9;
}
open.pop();
}
} else {
std::cout<<"FUCK\n";
}
}
//
// Parse in queries
//
for (int i = 0; i < queries; i++) {
getline(cin, str);
Tag t = Tag();
bool defined = false;
auto next_dot = str.find(".");
while (next_dot!=string::npos) {
string name = str.substr(0,next_dot);
if (defined && t.tags.find(name) == t.tags.end()) {
//TAG NOT IN T
cout<<"Not Found!"<<endl;
continue;
}
t = !defined ? tags[name] : t.tags[name];
defined = true;
str = str.substr(next_dot+1);
next_dot = str.find(".");
}
auto splitter = str.find("~");
string tag_name = str.substr(0,splitter);
string attrib_name = str.substr(splitter+1);
if (!defined) {
t = tags[tag_name];
} else if (t.tags.find(tag_name) == t.tags.end()) {
//TAG NOT IN T
cout<<"Not Found!"<<endl;
continue;
} else {
t = t.tags[tag_name];
}
// T is now set, check the attribute
if (t.attribs.find(attrib_name) == t.attribs.end()) {
cout<<"Not Found!"<<endl;
} else {
cout<<t.attribs[attrib_name]<<endl;
}
}
return 0;
}
我试过的
只需在上面的行中将 Tag x = t.tags[tag_name];
定义为新变量,然后执行 t = x;
即可解决此问题,但为什么会发生这种情况?
另外,下面的查询也会失败:a.b.c~height
,但是在第 99 行尝试获取 a.tags["b"] 时失败了。不知道为什么。我本来打算使用上面的 hacky 修复程序,但这似乎是我做错的一个大核心问题。
我会在 IDE 上建议 运行 并验证解析是否确实正确。
t=t.tags[tag_name]
此表达式不安全,因为您正在复制分配该对象拥有的对象而不是拥有对象。
考虑这一行发生了什么:
- 执行地图查找并returns一个
Tag&
。
- 您尝试将其复制分配给
t
,调用隐式复制分配运算符。
- 此运算符从复制源的
tags
属性复制分配 t.tags
-- 位于 t.tags
.
结果是您要复制到 t
中的对象在该副本的中间被销毁。这会导致未定义的行为,并且立即崩溃老实说是 最好的结果 ,因为它告诉您问题的确切位置。 (这种问题经常出现在程序后面的某个时刻,此时您已经失去了找出导致 UB 的原因所必需的状态。)
一种解决方法是将源对象移动到临时对象中,然后将该临时对象移动分配给 t
:
t = Tag{std::move(t.tags[tag_name])};
这会在我们尝试将其放入 t
之前从 t
中提取我们要分配给 t
的数据。然后,当 t
的赋值运算符替换 t.tags
时,您尝试分配给 t
的数据不再存在。
但是,这种总体方法涉及大量不必要的复制。最好将 t
声明为 Tag const *t;
,而不是将其声明为标记的 指针 。然后,您只需移动该指针以指向数据结构中的其他标记,而无需进行复制。
旁注:前几天我刚做了这道题!这里有一个提示可以帮助您简化事情:您真的需要标签结构吗?是否有更简单的查找结构类型可以代替嵌套标签?
所以问题说明了问题...
背景:
我正在尝试从 HackerRank 解决 this problem。
它基本上是一个 html 标签解析器。保证有效输入,属性仅为字符串。
我的方法
我创建了一个自定义 Tag
class,它可以存储其他 Tag
的 map<string,Tag>
,以及 map<string,string>
的属性。解析似乎工作正常。
问题
在查询部分,我在以下 query/html 组合中得到 BAD_ACCESS
错误:
4 1
<a value = "GoodVal">
<b value = "BadVal" size = "10">
</b>
</a>
a.b~size
当我尝试从 a
访问 b
标签时发生错误。具体在t=t.tags[tag_name]
下面第118行
代码
#include <cmath>
#include <cstdio>
#include <vector>
#include <iostream>
#include <algorithm>
#include <sstream>
#include <map>
#include <stack>
using namespace std;
class Tag {
public:
Tag(){};
Tag(string name):name(name){};
string name;
map<string,Tag> tags = map<string, Tag>();
map<string,string> attribs=map<string,string>();
};
int main() {
int lines, queries;
std::cin>>lines>>queries;
std:string str;
getline(cin, str);
stack<string> open;
auto tags = map<string, Tag>();
for (int i = 0; i < lines; i++) {
getline(cin, str);
if (str.length()>1){
// If it's not </tag>, then it's an opening tag
if (str[1] != '/') {
// Parse tag name
auto wordidx = str.find(" ");
if (wordidx == -1) {
wordidx = str.length()-1.f;
}
string name = str.substr(1,wordidx-1);
auto t = Tag(name);
string sub = str.substr(wordidx);
auto equalidx=sub.find("=");
// Parse Attributes
while (equalidx != std::string::npos) {
string key = sub.substr(1,equalidx-2);
sub = sub.substr(equalidx);
auto attrib_start = sub.find("\"");
sub = sub.substr(attrib_start+1);
auto attrib_end = sub.find("\"");
string val = sub.substr(0, attrib_end);
sub = sub.substr(attrib_end+1);
t.attribs[key] = val;
equalidx=sub.find("=");
}
// If we're in a tag, push to that, else push to the base tags
if (open.size() == 0) {
tags[name] = t;
} else {
tags[open.top()].tags[name]=t;
}
open.push(name);
} else {
// Pop the stack if we reached a closing tag
auto wordidx = str.find(">");
string name = str.substr(2,wordidx-2);
// Sanity check, but we're assuming valid input
if (name.compare(open.top())) {
cout<<"FUCK"<<name<<open.top()<<endl;
return 9;
}
open.pop();
}
} else {
std::cout<<"FUCK\n";
}
}
//
// Parse in queries
//
for (int i = 0; i < queries; i++) {
getline(cin, str);
Tag t = Tag();
bool defined = false;
auto next_dot = str.find(".");
while (next_dot!=string::npos) {
string name = str.substr(0,next_dot);
if (defined && t.tags.find(name) == t.tags.end()) {
//TAG NOT IN T
cout<<"Not Found!"<<endl;
continue;
}
t = !defined ? tags[name] : t.tags[name];
defined = true;
str = str.substr(next_dot+1);
next_dot = str.find(".");
}
auto splitter = str.find("~");
string tag_name = str.substr(0,splitter);
string attrib_name = str.substr(splitter+1);
if (!defined) {
t = tags[tag_name];
} else if (t.tags.find(tag_name) == t.tags.end()) {
//TAG NOT IN T
cout<<"Not Found!"<<endl;
continue;
} else {
t = t.tags[tag_name];
}
// T is now set, check the attribute
if (t.attribs.find(attrib_name) == t.attribs.end()) {
cout<<"Not Found!"<<endl;
} else {
cout<<t.attribs[attrib_name]<<endl;
}
}
return 0;
}
我试过的
只需在上面的行中将 Tag x = t.tags[tag_name];
定义为新变量,然后执行 t = x;
即可解决此问题,但为什么会发生这种情况?
另外,下面的查询也会失败:a.b.c~height
,但是在第 99 行尝试获取 a.tags["b"] 时失败了。不知道为什么。我本来打算使用上面的 hacky 修复程序,但这似乎是我做错的一个大核心问题。
我会在 IDE 上建议 运行 并验证解析是否确实正确。
t=t.tags[tag_name]
此表达式不安全,因为您正在复制分配该对象拥有的对象而不是拥有对象。
考虑这一行发生了什么:
- 执行地图查找并returns一个
Tag&
。 - 您尝试将其复制分配给
t
,调用隐式复制分配运算符。 - 此运算符从复制源的
tags
属性复制分配t.tags
-- 位于t.tags
.
结果是您要复制到 t
中的对象在该副本的中间被销毁。这会导致未定义的行为,并且立即崩溃老实说是 最好的结果 ,因为它告诉您问题的确切位置。 (这种问题经常出现在程序后面的某个时刻,此时您已经失去了找出导致 UB 的原因所必需的状态。)
一种解决方法是将源对象移动到临时对象中,然后将该临时对象移动分配给 t
:
t = Tag{std::move(t.tags[tag_name])};
这会在我们尝试将其放入 t
之前从 t
中提取我们要分配给 t
的数据。然后,当 t
的赋值运算符替换 t.tags
时,您尝试分配给 t
的数据不再存在。
但是,这种总体方法涉及大量不必要的复制。最好将 t
声明为 Tag const *t;
,而不是将其声明为标记的 指针 。然后,您只需移动该指针以指向数据结构中的其他标记,而无需进行复制。
旁注:前几天我刚做了这道题!这里有一个提示可以帮助您简化事情:您真的需要标签结构吗?是否有更简单的查找结构类型可以代替嵌套标签?