作者poyenc (发箍)
看板C_and_CPP
标题Re: [问题] 请问char**array的问题
时间Wed Nov 11 21:55:03 2020
※ 引述《Keitaro (动き出す时间...)》之铭言:
: 开发平台(Platform): (Ex: Win10, Linux, ...)
: Ubuntu 18.04 LTS
: 编译器(Ex: GCC, clang, VC++...)+目标环境(跟开发平台不同的话需列出)
: gcc
: 程式码(Code):(请善用置底文网页, 记得排版,禁止使用图档)
: https://ideone.com/jugcYC
: 重新上传完整原始码
後面恕删, 因为看起来不像 C++ 所以路过分享一下拙见.
C++ 物件不仅仅是一块记忆体, 因为生命周期中会有建构子/解构子参与, 简单
说就是至少会比 C 语言多两次函式呼叫. 所以撰写的时候
不会轻易地创造物件
, 更不会创完而不用它. array new/delete 这种昂贵的操作其实是蛮罕见的.
你的程式码主要存在两个问题:
1. ParseCmd() 的实作没有弹性, 且效率不高
2. 准备 execve() 引数时做了许多不必要的操作
一般为了减少对 STL container 的依赖,
函式介面会倾向於接受迭代器而不是
特定容器型别物件, 而且除非你很确定要用
std::char_traits<CharT>::eq()
来比较字元是否相等, 不然我们在搜寻的时候倾向於呼叫 STL algorithm 而非
std::basic_string 这种内嵌比较逻辑的型别, 所以通常介面会这样设计:
template <
typename ForwardIterator,
typename ForwardSentinel,
typename OutputIterator>
void ParseCmd(ForwardIterator first, ForwardSentinel last,
OutputIterator output) {
while (first != last) {
// call std::find() here
// other code goes here
(*output++) = std::string(first, last);
}
}
#include <iterator>
const std::string str =
"-vsync 0 -i file.cfg Compare.yuv";
std::vector<std::string> vCmdSet;
ParseCmd(begin(str), end(str), std::back_inserter(vCmdSet));
std::basic_string 只作为字元的载体, 而不是兼做比较的角色.
再来是准备 execve() 引数的逻辑, 如果非得用 array new/delete 来管理物
件, 那前面呼叫 ParseCmd() 创建一堆 std::string 物件有什麽用呢? 有没
有办法
重复使用这些物件的内容呢? 实际上你可以呼叫
std::string::data()
成员函式来达成这件事情, 然後把拿到的指标存进另一个 std::vector 内:
#include <algorithm>
#include <iterator>
std::vector<
char*> ppCmdArg;
std::transform(begin(vCmdSet), end(vCmdSet),
std::back_inserter(ppCmdArg),
[](std::string& s) {
return data(s); });
ppCmdArg.push_back(
nullptr);
execve(...,
data(ppCmdArg), ...);
这里用到的概念主要有两个: 1) std::vector 的
data() 成员函式会回传第
0 个元素的位址, 而且每个成员的位址保证是连续的, 所以回传的东西和你用
array new 创建出来的 array of pointers 是相同的; 2) 再者 std::string
的
data() 成员函式会回传第 0 个字元的位址, 每个字元的位址也是连续的,
而且回传的记忆体和 c_str() 相同
(只差在 constness), 包含结束字元.
下次撰码的时候可以
试着写出无 new/delete 的程式码, 因为偏底层的函式呼
叫次数愈多, 发生错误的机会也跟着变多.
--
[P1389R1] Standing Document for SG20: Guidelines for Teaching
C++ to Beginners
https://wg21.link/p1389r1
SG20 Education and Recommended Videos for Teaching C++
https://www.cjdb.com.au/sg20-and-videos
--
※ 发信站: 批踢踢实业坊(ptt.cc), 来自: 123.193.76.216 (台湾)
※ 文章网址: https://webptt.com/cn.aspx?n=bbs/C_and_CPP/M.1605102909.A.FC2.html
1F:→ Lipraxde: new 的问题是会产生 raw pointer,要主动用 delete 释 11/11 22:28
2F:→ Lipraxde: 放资源。至於昂贵吗...难道 vector、string 就不做 hea 11/11 22:28
3F:→ Lipraxde: p allocation 了? 11/11 22:28
STL container 预设会用 std::allocator<T> 来做配置, 以物件的生命周期来
看, 呼叫的介面先後顺序如下:
1. allocate()
2. construct()
3. destroy()
4. deallocate()
1 ~ 2 的使用效果和 operator new + placement new 相同, 并非使用 array
new, 差别在於元素是需要用到的时候才建构, 而非一次全部建构好, 这也是为
什麽如 std::vector 会有 capacity() 及 size() 两种不同用途的成员函式.
说 new 会产生 raw pointer 那不会是什麽太大问题, 因为使用者可以选择重
载 new/delete, 只要在重载版本里做好物件管理即可.
4F:→ CoNsTaR: 还是看使用情景吧,server 可能就会想要一次全部建构好 11/11 23:58
5F:推 Keitaro: 谢谢板上各位先进的指导 11/12 03:48
6F:→ Keitaro: 虽然有很多东西不太了解 我在努力查资料学习 11/12 03:49
7F:→ Keitaro: 让各位花这麽多时间指导小弟 真的非常感谢 11/12 03:50
8F:推 ucrxzero: 是说内文说用s.data()可是代码用data(s)是我看错吗? 11/12 10:10
是的, 你没看错
9F:→ Lipraxde: 我主要是不明白为什麽你文内会写说 new/delete 昂贵( 11/12 22:49
10F:→ Lipraxde: 还是 array 这个字眼在这边很重要?)。 11/12 22:49
你知道 operator new 以及 operator new[]
(俗称 array new) 的差异吗?
遇到没有 default constructor 的型别初始化会怎麽进行?
11F:→ Lipraxde: 我怎麽觉得是用法的问题,而不是它本身昂贵? 11/13 01:56
昂贵的原因在於物件的建构方式.
如同阵列般, 元素的建构是彼此相依的, 除了获取记忆体的时间成本; 也额外
付出了第 1 个元素开始建构到第 n - 1 个元素结束建构的时间
(n 为阵列大
小), 我们才能开始存取第 0 个元素.
为了使用 operator new[] 来建构大量物件, 使用者还必须提供成本较低的
default ctor, 让型别支援 two-phase initialization, 也提高程式码复杂度
所以
物件分开建构与否直接影响程式的执行效率, 从 C++17 开始新增多种记忆
体管理介面如
std::byte,
std::uninitialized_*() 系列函式, 即是为了完善
这部分的开发.
12F:推 Hurricaneger: 看不懂就推 11/13 10:40
13F:→ Lipraxde: 喔~所以说,就是这些多呼叫的 default constructor 造 11/13 11:30
14F:→ Lipraxde: 成它比较贵,感到豁然开朗!我以前基本上都当 default 11/13 11:30
15F:→ Lipraxde: constructor 没什麽成本 Orz 11/13 11:30
如果是 trival default ctor 就不会有什麽成本.
但从 C++11 开始有执行绪的观念; C++17 开始有 executor 平行处理的观念,
在使用 operator new[] 这类操作以前都要思考是否有必要排队循序执行.
※ 编辑: poyenc (61.216.75.43 台湾), 11/13/2020 12:04:52
16F:推 F04E: 这种优文竟然只有三推 11/13 13:03
17F:推 Lipraxde: 谢谢大大的指导 11/13 22:23