李云龙
不仅学到了format,还在老师的项目代码中学到了不同风格的时间处理
作者回复:比心
2024-01-17
1
peter
请教老师几个问题:
Q1:“假设 f(x,g) 的定义为 g(x)”,这句话表面上理解是:g(x)=f(x,g)。但好像说不通啊。f(x,g)的参数g的定义为g(x),是不是这样啊。
Q2:auto是由编译器来自动判断类型吗?(类似于弱类型语言了)
Q3:迭代器与只读迭代器有什么区别?难道迭代器除了“读”还可以“写”吗? 另外,逆向迭代器,比如begin,正常是从第一个向后面遍历,那“逆向”难道会向前遍历?(已经是第一个,不可能向前遍历啊)
Q3:代码的运行环境是什么样的?
对于实例代码,想运行一下看看,IDE是什么啊,包括设置编译器版本为c++20等。
Q4:“start = std::find(getArray().begin(), getArray().end(), 1);”调用后为什么会变成悬空指针?能否再详细说明一下?
作者回复:Q1:这里的意思是f(x,g)=g(x),函数f包含两个参数,x是一个普通参数,g是另一个函数。函数g的定义是g(x)=…(定义未知),所以根据函数f的定义会调用g,并将x作为参数传递给g。这里f就是一个高阶函数了。
Q2:auto是编译时自动类型推断,虽然可以减少开发者定义变量的代码量,但和弱类型语言的动态类型天差地别。弱类型语言更多的是动态类型,也就是在运行时执行后才能判断这个变量的类型(比如一个弱类型语言的函数的返回值类型可能是两个不相关的类型),而C++是强类型语言,auto的作用是在编译时根据函数的定义自动推断出类型,也就是必须要求C++编译器能够根据代码在编译时静态确定类型,C++的普通函数也不可能返回两个不相干类型,这一点在C++里是通过模板实现的,但是模板的原理也是编译时实例化模板函数实现的,这样才能编译时静态确定类型,所以auto和弱类型的动态类型是完全不同的。
Q3:迭代器的只读指的是是否能够通过迭代器去修改迭代器指向的数据,迭代器就是一个泛化的“指针”,所谓的只读迭代器就类似于const T *,可以修改迭代器,但是不能修改迭代器指向的数据。所谓的逆向迭代器就是会将序列的最后一个元素指针作为begin,这种迭代器会将对迭代器的累加操作转换为对迭代器内部指针的累减操作,实现逆向迭代。
Q4:代码的运行环境推荐Windows下的Visual C++ 2022,对C++20已有标准的兼容性是比较好的。
Q5:因为getArray返回的是一个临时对象,然后begin和end是在getArray返回的临时对象上执行的,所以begin和end执行结束后相应的临时对象都会被自动销毁,所以调用后begin和end返回的迭代器就变成了悬空的迭代器(包含悬空指针)。
如果用伪代码解释,这个过程就是:
a1 = getArray();
b = a1.begin();
arg1 = b;
a2 = getArray();
e = a2.begin();
arg2 = e;
DESTROY(a1);
DESTROY(a2);
DESTROY(b);
DESTROY(e);
start = std::find(REF(arg1), REF(arg2), 1);
所以真的调用find的时候a1和a2都已经被销毁了,自然arg1和arg2两个迭代器就“悬空”了。
2023-02-09
1
阿阳
前端开发中,JavaScript的模块化方案发展多年了,历史中也出现了各种模块化方案,浏览器现在支持es module方案。感觉js的模块化探索走在前列。现在不太理解编译型语言的模块化和js这种解释型语言的模块化,有什么异同?
作者回复:在ES Module之前,JS的模块化方案主要是各类“事实标准”,整体混乱且问题多多,ES Module的定案也花费了相当之多的时间,浏览器能够支持ES Module也是近些年的事情了,出于兼容性考量大部分前端项目还会沿用类似于webpack的方案。
而C++由于更多的历史遗留问题、技术限制甚至是标准委员会内不同大公司之间的一些博弈,导致Modules标准到C++20才得以定案。
模块化的目的就是名称(符号)隔离,并且支持控制模块内名称(符号)的可见性,从这个目的上无论是解释性语言和编译性语言其实是一致的,不同点在于实现原理、实现限制与因语言本身设计思路以及历史遗留问题导致的设计与实现的差异,这些都体现在具体的设计细节与实现技术细节上,就算不同编译性语言之间(比如Java与Rust)与不同解释性语言之间(比如Python与ES)也会有很大差异,但是本质的设计目的一致,所以基本设计构思是不会有差别的。
2023-02-11
1
黄骏
内容太干了。哈哈。
nits:concepts.cpp的line 38应该是IteratorMemberFunction(&T::end)吧?
作者回复:是的,应该把begin换成end。
已进行修正。 🥰
2023-01-29
1
Geek_7c0961
concepts 这个feature太强了, 让我想起了rust中的traits 和 traits objects.
作者回复:Concepts的确是非常重要的feature,让C++编译时静态化编码整体提升了一个档次。现代C++演进有一种“Rust化”的趋势,当然了,C++还是有着独特的气质,而且对向前兼容性方面几乎是无敌的。
2023-01-31
tanatang
写面向对象的C++,成员和函数都在设计在类中,禁止使用这种不属于任何类的全局函数, 全局变量。
作者回复:OOP原教旨主义 👍
2023-01-18
5
Ak
怎么感觉C++越来越像Java和python😅
作者回复:C++的演进过程必定要不断借鉴其他的语言模型中的优点,这样才能不断现代化,在保持更高的性能的同时提升生产力。但是,C++跟Java和Python还是存在一个根本性区别,就是C++在演进过程中始终秉持着这样一个思想,即能在编译时解决的计算问题,就一定不要推迟到运行时。所以可以看到,自C++11之后推出的几乎所有新特性,都是为编译时服务的。这也是C++与其他编程语言之间最大的区别之一。
2023-01-18
5
Geek_7c0961
"这也能大量减少编译单元之间的符号冲突问题,毕竟可能出现,两个编译单元定义了同名,但只想在编译单元内部使用函数的情况,我们并不想给这些函数加上冗长的前缀。那这个时候,只需要使用 static 修饰符。比如我们可以在 A 和 B 中都定义 static 函数 to_int,然后再编译链接,这样就不会出现符号冲突的问题。" 这块儿能否给个具体的代码示例?
作者回复:比如以下代码
a.cpp:
#include <iostream>
static void print() {
std::cout << "Print in A" << std::endl;
}
void fa() {
print();
}
b.cpp:
#include <iostream>
static void print() {
std::cout << "Print in B" << std::endl;
}
void fb() {
print();
}
main.cpp
extern void fa();
extern void fa();
int main() {
fa();
fb();
return 0;
}
这里a.cpp和b.cpp两个编译单元都有print函数,但是函数使用了static修饰符,因此print函数仅对各自编译单元内部可见,所以链接时不会导致符号冲突问题。会正确输出:
Print in A
Print in B
2023-01-18
2
wilby
怎么编译这个项目呢?在macOS下没试出来怎么编译
作者回复:目前来说,对C++20后后续演进标准的支持,Visual C++ 支持最好。macOS默认使用 Xcode 提供的 clang (llvm),此外,gcc、clang对新标准的支持存在一定差异。你可以稍后参考我分享的代码仓库来使用CMake尝试编译(即将提供)。
也欢迎提供反馈,甚至是PR
2023-01-20
2
tang_ming_wu
更新周期是怎样的?下一期啥时候,期待。。。
编辑回复:每周三篇更新,具体是周一、三、周五的零点更新。推荐加入学习计划,跟着更新同步学习。期待留言区看到你更多的留言!
另外,等待更新期间尝试下第一讲的课后思考题呗,多学多练,效果加倍哈哈!
2023-01-17
编辑推荐
讲师的其他课程
包含这门课的学习路径
C++工程师
7门课程 51.6w人学习
看过的人还看了