C++中结构体属性秒变数组元素:高效访问技巧揭秘
如何将结构体的属性当作数组元素访问?
在C++中,我们可能会用一个由多个字符串组成的类或结构体来表示一个URL,如下所示:
struct basic {
std::string protocol;
std::string username;
std::string password;
std::string hostname;
std::string port;
std::string pathname;
std::string search;
std::string hash;
};
为了将每个组件与一个索引关联起来,我们可以使用一个enum class
:
enum class component {
PROTOCOL = 0,
USERNAME = 1,
PASSWORD = 2,
HOSTNAME = 3,
PORT = 4,
PATHNAME = 5,
SEARCH = 6,
HASH = 7,
};
最初,你可能会使用switch语句来访问组件:
std::string& get_component(basic& url, component comp) {
switch (comp) {
case component::PROTOCOL: return url.protocol;
case component::USERNAME: return url.username;
case component::PASSWORD: return url.password;
case component::HOSTNAME: return url.hostname;
case component::PORT: return url.port;
case component::PATHNAME: return url.pathname;
case component::SEARCH: return url.search;
case component::HASH: return url.hash;
}
}
如果频繁访问,switch case可能会引入性能开销。为了更好的性能,我们可以将组件存储在一个数组中:
struct fat {
std::array<:string> data;, 8>
std::string &protocol = data[0];
std::string &username = data[1];
std::string &password = data[2];
std::string &hostname = data[3];
std::string &port = data[4];
std::string &pathname = data[5];
std::string &search = data[6];
std::string &hash = data[7];
};
或者,你可以避免硬编码索引:
struct fat {
std::array<:string> data;, 8>
std::string& protocol = data[int(Component::PROTOCOL)];
std::string& username = data[int(Component::USERNAME)];
std::string& password = data[int(Component::PASSWORD)];
std::string& hostname = data[int(Component::HOSTNAME)];
std::string& port = data[int(Component::PORT)];
std::string& pathname = data[int(Component::PATHNAME)];
std::string& search = data[int(Component::SEARCH)];
std::string& hash = data[int(Component::HASH)];
};
有了这个新的数据结构,通过索引获取组件变得更简单:
std::string& get_component(fat& url, component comp) {
return url.data[int(comp)];
};
不幸的是,每个引用在新的fat数据结构中可能在64位系统上占用8字节。如果你不经常创建数据结构的实例,这不是问题。但如果你需要频繁创建实例,你可能想要避免使用引用。你可以尝试用简单的方法替换引用:
struct advanced {
std::array<:string> data;, 8>
std::string &protocol() { return data[0]; }
std::string &username() { return data[1]; }
std::string &password() { return data[2]; }
std::string &hostname() { return data[3]; }
std::string &port() { return data[4]; }
std::string &pathname() { return data[5]; }
std::string &search() { return data[6]; }
std::string &hash() { return data[7]; }
};
这并不完全令人满意,因为它需要调用方法而不是访问属性。Eugene Ostroukhov有一个更好的方法,使用指向成员的指针操作符。我们创建一个编译时数组,包含指向basic
结构体属性的指针:
std::string& get_component_fast(basic& url, component comp) {
constexpr static std::array accessors = {&basic::protocol,
&basic::username, &basic::password, &basic::hostname,
&basic::port, &basic::port, &basic::pathname,
&basic::search, &basic::hash};
return url.*accessors[int(comp)];
}
这样,你就不需要修改原始类,而且很可能获得极好的性能。