求教个 C++ Get 函数怎么写的问题

42 天前
 Betsy

代码

#include<iostream>
#include <cstdint>
#include <unordered_map>

enum struct Status {
    kOk = 0,
};

struct Student {
    std::string name;
    std::size_t age;
};

class Table {
   public:
    Table() {
        this->map_.insert(std::make_pair("w1", Student("li", 23)));
        this->map_.insert(std::make_pair("s2", Student("zhao", 18)));
    }

    Status Get(const std::string& key, Student* value) {
        *value = this->map_[key];
        return Status::kOk;
    }

    Student Get(const std::string& key) { return this->map_[key]; }

   private:
    std::unordered_map<std::string, Student> map_;
};

int main(int argc, char* argv[]) {
    Table table;

    Student stu1;
    const Status& status = table.Get("w1", &stu1);
    std::cout << stu1.name << ":" << stu1.age << std::endl;

    const Student& stu2 = table.Get("s2");
    std::cout << stu2.name << ":" << stu2.age << std::endl;
    return 0;
}

结果

li:23
zhao:18

问题

1. Status Get(const std::string& key, Student* value);
2. Student Get(const std::string& key);

在 Java/Python 等语言中,个人更喜欢第 2 种写法;但是 C++ 中,一些项目更倾向于第 1 种写法,为啥呢?这样有什么好处吗?

2237 次点击
所在节点    C++
36 条回复
fgwmlhdkkkw
42 天前
c++的逻辑上“谁申请的谁释放”。
sagaxu
42 天前
两个区别,一个是内存管理,一个是错误码。

Java/Python 没有那么细致的内存管理,比如 C++中,可以栈上分配 Student ,并且复用
tool2dx
42 天前
我也喜欢第二种写法。第一种写法是偏向编译效率,以前 C++还没有&&和 move ,所以会多一次临时拷贝。

但其实不写游戏,不考虑效率,第二种更便于理解。
fpk5
42 天前
C++有一个问题就是怎么区分错误和正常返回,不像 Java 和 Python ,C++的 exception 不一定 work as expected 。你的例子里的 unordered_map::operator[]是有可能失败的(比如内存不足),你的 caller 不一定能正确处理这种 exception 。

第一种写法更接近于 C 风格,返回值用于确定是否有错误,传入一个参数用于接收真正的返回值。很多公司会自己规定使用哪种写法,比如 Google 内部 C++规范实现了一个 StatusOr<T>的类型可以用一个返回值同时表示是否错误和实际返回值。
tyzandhr
42 天前
要是 std23 ,可以用 expected<Student>
fgwmlhdkkkw
42 天前
@fgwmlhdkkkw 我没仔细看代码🤪,第二种写法遇到不存在的 key 就完蛋了呀
fgwmlhdkkkw
42 天前
不是,第一种也不对呀🫵
iceheart
42 天前
Get 语义不太清晰,个人喜欢 fetch
bool fetch(const string &, valuetype &);
或者:
valuetype *fetch(const string &);
henix
42 天前
首先,这两种写法语义上并不等价,第一种写法多出一个 Status ,第二种写法要加上 Status 的话得返回一个 std::tuple<Status, Student> 或 std::variant<Status, Student>

两者的区别在于,第一种写法,Student 占用的内存由调用方分配,适用于对性能要求较高的场景;第二种写法,每调用一次 Get ,都会为返回的 Student 分配内存(尤其是 Student 包含了一个 string ,string 是动态分配),好处是用起来更方便。

考虑在一个循环中调用 Get ,如果用第一种写法,可以在循环外初始化 Student 并且复用 Student ,从而减少内存分配次数:

Student stu;
for (...) {
Get(key, &stu);
}
wnpllrzodiac
42 天前
第二种有临时变量效率不高,如果用引用,又有失效问题,当这个类释放后,get 传递的 引用变无效了
Betsy
41 天前
@sagaxu 这个如何复用?
Betsy
41 天前
@tool2dx 对,我也偏好第二种。
Betsy
41 天前
@tyzandhr 不需要这么高端吧,普世意义上的 C++
Betsy
41 天前
@fgwmlhdkkkw 第一种哪里不对?除过没判断 key 值是否存在导致潜在的 exception 之外。
Betsy
41 天前
@wnpllrzodiac
1. 不是有 RVO 嘛,两个执行效率差不多的吧。
2. 好像是会有这么个问题
MoYi123
41 天前
一般用
const Student& Get(const std::string& key) const { return this->map_.at(key); }
这样拷贝构造发生在外部.

如果有需要再加上 Student& Get(const std::string& key) const { return this->map_.at(key); }
可以支持 move

Status Get(const std::string& key, Student* value); 这种写法是 C 语言的风格. 不建议用
nevermoreluo
41 天前
写了一堆又删掉了,再次看到这个帖子还是忍不住想说点什么
以下仅个人观点
Student Get(const std::string& key) { return this->map_[key]; } 抛开内存效率不谈这个接口都不好

我最开始也觉得为什么不跟 py 一样直接返回对象呢,其实是因为 map_[key]这个用法和异常处理不一样。
map_[key]这个操作会在 key 不存在时构造一个,而 py 会返回 KeyError 。
那么既然报错了你就要处理,所以 py 这里的 KeyError 的异常其实隐式表达了 Status 中 NotFound 的概念。
另外我个人觉得这个不存在时构造一个是个定时炸弹,不要在拉屎后盖上沙子,否则可能要在某个午后一堆人找屎
jones2000
41 天前
都用指针不是效率更高吗。

std::unordered_map<std::string, Student*> map_;

Student* Get(const std::string& key) { return this->map_[key]; }
lovelylain
41 天前
this->map_[key] 当 key 不存在时会自动插入并返回,修改了 map 不符合 Get 语义,改为
const Student* Get(const std::string& key) const;
存在返回 value 地址,不存在返回 nullptr:
1. 避免修改 map
2. 避免拷贝
GeruzoniAnsasu
41 天前
orz

完美体现 c++有多复杂的例子。
可以去考虑的点:

- key 用 string 接收还是 string_view 接收? 后者支持从一段 parse 后的文本中提取一段作为 key
- student 返回时要不要创建单独的生命周期?如何保证/需不需要保证返回的 student 引用(指针)一定有效
- 异常处理范式用什么? 是错误码还是 optional 还是 expected 还是抛异常
- get 接口适不适合定义为 const ? 如果 const 的话返回的对象将不可修改,如果要进行二次处理则会引入额外复制,如果不 const 的话会存在非预期地修改了原 map 的隐患,破坏 get 的语义
- 多种 get 方式适不适合作为重载实现,还是重命名成不同的 get_xxx 比较好

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/1063354

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX