跳转至内容
  • A place to talk about whatever you want

    19 主题
    153 帖子
    SPeakS

    @johanvx 这个也是非常经典的公开课 中国大学Mooc和B站上应该都能找到视频

  • 7 主题
    20 帖子
    SPeakS

    @dustchens 链表结构损坏, 不闭环了 (如果问题解决可以把帖子状态设置为已解决

  • 开源软件 | 开源社区 | 开源理念 | 开源与商业 | 开源可持续发展 等相关话的交流讨论
    注: 这里的"开源"是泛化的共建共享概念, 范围包含 OSI的范围、自由软件、CC等相关内容

    56 主题
    239 帖子
    SPeakS

    @saul-goodman-lja 嗯嗯, 可能是gitee网络服务的问题

  • 37 主题
    165 帖子
    dustchensD

    感觉学习最难的地方就是内容太分散了,而且夹杂了很多和语言本身不相关的内容,可以说相关也可以说不相关,我的用词不一定准确。

    如果只限定C++20以上版本。完全用模块,且限定一种编译器,使用cmake,那入门的难度会降不少,但是中间还是有鸿沟,如果不能完全理解其中的历史包袱,还有大量使用,会非常难学。

    拿教培最成功的Java来说,初期学习很大程度是倾向于了解语言本身语法,还有一些实现特性,比如各种容器怎么用,特性背后是什么原理,IDEA一开,按钮点一点就可以了,并且出现问题也很容易定位错误,反正大伙都是随地大小抛异常的,叫什么名一清二楚。再深入一点,要学习各种轮子是怎么写的,ctrl按住鼠标点一点就跳进了源代码,源码非常清晰,顶多长了一点复杂了一点,抄也很容易抄明白。到了深入学习的时候,开始学习项目框架,多线程这些,再引入三方库,基本上很容易操作。最后的最后,哪怕学习不明白,了解的不够深入,也有spring这样的大杀器,它的原理很复杂,但是使用却很简单不同层级上@几下,很快就能搭建一个破烂项目,虽然很破烂,但是它跟高级项目是同一个东西,恭喜你,没有入门也半只脚踏进去了。

    但是C++不一样,一起跟它出现的是C,那就引出了无数的历史包袱,想要入门,门在哪里就成了一个问题,门在水面下,很多东西是以思想的形式存在的,并且一些新特性,本身就是散开的,都是为了解决之前分散在不同方面的问题,学了知道了但是不知道在哪串起来。
    第一个要学的就是内存管理的思想,为什么说是思想,而不是方法。我认为编程语言就是一步步抽象的过程,为什么要抽象,是因为要实现功能,很多写C的老保觉得底层更高级,以至于衍生出多汇编吹,不是这样的,计算机最终目的是实现人想要实现的东西,这个实现才是有意义的,编程语言实现的是语义,语义经过计算机执行得到结果,语义是我们书写最重要的东西。用什么汇编实现,底层到底怎么样,是次要矛盾!如果它是主要矛盾,那么大伙都应该在用 0 1实现语义才对,哪轮得到汇编,正因如此,表达语义更简单的汇编取代了打纸带写01,高级语言用更方便的语义表达让程序员能更好工作,AI也提供了用自然语言表达语义得到计算结果的能力。只不过我们这个领域大部分功能还是需要我们用编程语言实现语义,得到结果,或许几百年后编程可以用自然语言,直接通过更高级的编译器翻译成C++或者其他语言再变成机器码实现语义,得到程序。
    扯远了。。。。内存管理是一种思想,因此用虚拟机管理内存和手动管理内存,并没有本质的区别,只是实现不同,效果有略微差别罢了。C++处于一种半自动管理的状态,这个半自动是指相比于纯C,语言本身的机制自动帮你实现了部分管理内存的语义,RAII的思想就是这个半自动的衍生品。内存管理这部分要让新手能入门,就不能只讲构造析构,还有智能指针这些,而是要给出对应C语言在原始手动状态,是如何实现的,这样才能完全明白原理。
    就比如智能指针,它实现内存管理是借助了析构函数这个能在离开作用域自动调用的特性,相同的C语言代码,要借助一系列黑魔法才能实现同样的语义,给出这些代码有了对比就能更好讲解,让大伙知其所以然。
    再比如各种vector轮子的讲解,初学的时候一头雾水,为什么要用什么allocate和定位new,这时候把C实现new的代码贴出来,(先申请个空间,强转一下;对不同类型赋值,有的还需要再申请资源)这就又把new和delete是两件事的知识点再串起来了,然后又能引出这样拆分的好处:预先分配大块内存,定位new可以在指定位置用构造函数赋值。而主动调用析构,也就是通知房子空了,里面可能存在的资源也清理了。如果不拆分,那调用delete,就会把大块内存中间删掉一个内存,释放给操作系统,还要维护断开不连续的两个内存。如果项目能以一种思想的实现串连起分散的内容,或者按片划分,那对于学习真的很有帮助。

    然后就是各种历史遗留问题,enum、头文件本质是复制粘贴,其实就是那时候编译器不太智能硬件各种受限,导致后续一系列。。。。然后还要讲几个编译器,cmake,包管理,这些感觉都不是语言本身要学习的,而是历史遗留不得不学,讲解cmake这些东西,那就要对比着来,比如在vs里添加各种链接,才能体现有这么个配置文本的好处。扯不出来了,总之这些历史遗留和基础设施,弗如rust。后续建议扯不出来了

  • 19 主题
    50 帖子
    SPeakS
    文章导读 一、类模板与模板特化(全特化与偏特化) 二、类模板与其模板特化的应用 三、模板特化存在性问题 一、类模板与模板特化(全特化与偏特化)

    从接受类型的角度
    类模板:全集R
    模板偏特化(部分特化):为全集的一个子集A
    模板全特化:为R中的一个"点",或者说为R中的一个元素
    匹配规则:越特化匹配优先级越高(见下面例子)

    98a9da6a-01af-4fc4-83ad-91b7ca549699-image.png

    1.类模板

    可以接受任意类型

    // R template<typename T> class A {}; // 类模板是能接受任意类型,A后面不需要(不能)任何处理 2.模板偏特化(局部特化)

    可以接受任意指针类型

    // A template<typename T> class A<T *> {}; // 类模板A的偏特化版本,在A后指出特化的范围 3.模板全特化

    指定接受int类型

    template<> class A<int> {} // 类模板A的全特化版本(已经是类模板的一个实例了),在A后直接指出明确类型int 4.例子: #include <iostream> #include <string> using namespace std; template<typename T> class A { public: A() { cout << "R" << endl; } }; /* 注释1 -- 接受指针 template<typename T> class A<T *> { public: A() { cout << "A" << endl; } }; */ /* 注释2 -- 接受int template<> class A<int> { public: A() { cout << "int" << endl; } }; */ int main() { A<string> r; // 1 A<char *> a; // 2 A<int> i; // 3 return 0; } 5.测试不同情况:

    当只有一个类模板(可接受任意类型R)存在时,1, 2, 3都使用类模板实例化

    v2-a53836d6d2a913d69c2fd90b0dd4371f_1440w.png

    注释1是接受所以指针类型(R的子集A),所以称其为类模板A的偏特化(范围特化)。即把类模版A所能接受的指针类型单独处理(实例化)。当取消注释1时:char * 将由这个模板类A的偏特化版本(范围特化,局部特化)处理。

    v2-2bd02b7f6a4dbb5cdbc586f98d76b92b_1440w.png

    注释2只接受int类型的参数(可以看出是全局R中的一个元素)。当你用int实例化A时,将会由这个全特化版本来实现(而不会使用可以接受任意类型的版本(泛化版))。

    v2-6a08120bfe6da8471d8fb374b2665a65_1440w.png

    二、类模板与其模板特化的应用 用模板的偏特化 实现一个 能移除任意类型const属性的模板类:remove_const 主要功能和用法: 功能: 给remove_const一个类型后 --1.(情况1)如果这个类型没有const属性则获得这个类型本身。 --2.(情况2)如果这个类型有const属性则移除它。 用法: remove_const<Type>::type 使用场景: 当拿到一个未知变量时,想获得这个变量(或对象)的非const的类型 1.实现情况1

    似乎没做什么事,aa是int符合情况1。但情况2不符合,bb没有变成int。

    #include <iostream> #include <type_traits> template<typename T> struct remove_const { using type = T; }; int main() { int a = 1; const int b = 2; remove_const<decltype(a)>::type aa = 3; remove_const<decltype(b)>::type bb = 4; std::cout << std::is_same<decltype(aa), int>::value << std::endl; std::cout << std::is_same<decltype(bb), int>::value << std::endl; return 0; }

    运行结果:
    v2-7a36de2df8473699ecc173f27351c876_1440w.png

    2.实现情况2

    从上面的实现可以看出,当传给模板的参数是带const类型时它还会返回带const属性的类型。同时从remove_const的定义也可以看出他是个 复读机 你给他什么类型他就给你什么类型。

    这时候可以使用上面介绍的偏特化的性质,来把带有带const的类型这个子集从全集中分离出来 特殊处理。如下:

    #include <iostream> #include <type_traits> template<typename T> struct remove_const { using type = T; }; template<typename T> struct remove_const<const T> { using type = T; }; int main() { int a = 1; const int b = 2; remove_const<decltype(a)>::type aa = 3; remove_const<decltype(b)>::type bb = 4; std::cout << std::is_same<decltype(aa), int>::value << std::endl; std::cout << std::is_same<decltype(bb), int>::value << std::endl; return 0; }

    运行结果:

    给带const的类型,写了一个特化版本。所以当remove_const接受一个带const的类型时,就会通过这个偏特化版本实例化,由于这个偏特化版本把const从类型中分离出来了,则这里的T就是没有const的类型,从而实现去除类型const的功能。

    v2-d337e4dc338fc9a90fcfa7ef9db27804_1440w.png

    三、模板特化存在性问题

    一个类模板的特化,是对某一个类模板的子集做特化处理的。而它不能"独立存在"。既只有存在一个类模板X, 才能存在对它特化的版本。

    template<typename T> class A<T *> {}; int main() { return 0; }

    v2-1023f2d6ec20e5efa6300629068527d6_1440w.png

  • 一个技术知识分享、学习、交流的社区

    15 主题
    46 帖子
    sunrisepeakS

    @johanvx 版块已创建, 可以检查确认一下是否有话题贴/Topic工具的权限

    https://forum.d2learn.org/category/21/blog-johan-xie
  • Got a question? Ask away!

    4 主题
    14 帖子
    SPeakS

    备注一下使数学公式的使用语法

    单行公式语法 - $ 你的公式 $

    $ log_2^n $

    $ log_2^n $

    多行公式语法 - $$ 你的公式 $$

    $$ log_2^n => log_2^9 = 3 , n = 9 $$

    $$
    log_2^n =>
    log_2^9 = 3, n = 9
    $$

公告栏 | Bulletin Board

欢迎加入d2learn社区 - 社区指南
Welcome to the d2learn Community - Community Guide

一个以 [知识、技术、代码、项目、想法、开源] 相关话题为主导的社区
A community focused on topics related to [knowledge, technology, code, projects, ideas, and open source].


在线用户