C++学习记录

前言

  • 此处主要记录,c++面向对象这部分知识,有助于快速上手其他语言!
  • 永远要记住:限制我们发展的永远是我们自己的认知,而不是别人。
  • 心态:同样的环境,同样的氛围,总有人比我们发展的更好,总有人发展的不如我们。
  • 首推的学习文档:https://en.cppreference.com/w/

1. 从c到c++,概述

  • c++是c的超集,C++增加了很多的头文件,不要在意130
    在这里插入图片描述
  • C++包括四种编程范式:面向过程(STL)、面向对象(类和对象)、泛型编程(模板)、函数式编程(Lambda)
  • C++Primer推荐序中,有的说是:C风格、基于对象、面向对象、泛型(加上微软的COM就是基于组件);也有的说是:面向过程、基于对象、面向对象、泛型和函数式
  • 实现语言的时候,大多数情况下使用的是面向过程、面向对象的范式;当相关的泛型能提高开发效率的情况下,才考虑使用(泛型、函数式编程)。
  • 按照编程范式学习其他编程语言,如java.
  • 按照编程范式进行分门别类的学习

# 一、面向过程:STL

queue类

stack类

deque(double-endend queue)

  • 这部分很灵活,可以用来实现栈,队列等

Strings library

unordered_map

  • 提供了非排序,使用哈希表实现;
  • map是排序,使用了红黑树实现

编码规范

百度+谷歌
阿里+谷歌

习题练习

#245. 货仓选址->std::vector

#include<iostream>
#include<algorithm>
#include<vector>
using namespace std;

int main() {

    int N,input;
    vector<int> vec;

    cin >> N;
    for(int i = 0; i < N; i++) {
        cin >> input;
        vec.push_back(input);
    }
    sort(vec.begin(), vec.end());
    int pos = vec[N / 2], sum = 0;
    for(int i = 0; i < N; i++) {
        sum += abs(vec[i] -pos);
    }

    cout << sum << endl;
}

# 二、面向对象

  • 封装:我该有的和我该做的
  • 继承:叫一声爸爸,开启财富之门
  • 多态:我就是我,是不一样的烟火

## 封装

类和对象

Fundamental types

类型与变量

类型 = 类型 数据 + 类型 操作

成员属性与方法

C++ class与c struct相比,c++ class 中可以放成员方法,而struct不行。
C 语言的struct有单独的命名空间,所以你甚至可以这样定义一个跟类型名字一样的变量;

访问控制权限:public、private、protected、friend

  • 强调作用范围:控制类外对类内的访问权限。(此处好好理解类内类外)
  • public、private、friend的作用:如,类外重载运算符时,就需要使用friend友元进行声明修饰
  • protected简介

命名空间:namespace

  • 和java、systemverilog中的package是一个道理,防止命名空间内各命名、标识符冲突。
  • 实现一个cout
  • cin、cout也是c++实现的一个对象,不是一个类型也不是一个方法,本质上就是一个对象

构造函数与析构函数

  • 构造函数:默认构造函数、有参构造函数、转换构造函数、拷贝构造函数
  • 转换构造函数(一个参数的有参构造函数):普通的转换构造函数的应用、函数传值过程中的转换构造函数的作用
  • 拷贝构造函数:拷贝构造函数的基本使用、拷贝构造为什么传入const引用(因为可能需要处理临时的中间变量,所以传入const;防止套娃现象出现,采用引用传入)、赋值拷贝操作
  • 还有一个移动构造函数,c++11帮助c++重回神坛,c++11之前没有将左值和右值区分开。
  • 一个有参构造函数也被称为转换构造函数,
  • 拷贝构造和拷贝赋值运算符是完全不一样的
  • 析构函数:需要资源回收的时候,就需要析构函数
  • 工程项目中,构造函数和析构函数越简单越好,会使用伪构造函数伪析构函数,体会工厂模式
  • =default=delete的作用:明确指出使用或不使用默认的构造函数
    C++学习重点:程序处理流程

3/5法则

  1. 需要析构函数的类也需要拷贝构造函数和拷贝赋值函数。(当类中有些资源是动态申请的,那么它才需要资源回收,可能需要析构函数;但是,对于动态申请出来的这些资源,在发生拷贝行为的时候,通常情况下是发生深拷贝)
  2. 需要拷贝操作的类也需要赋值操作,反之亦然。(当我们为某个类实现拷贝构造的时候,说明这个类一定是有一些特殊的行为,而这种特殊行为也应该被继承在赋值拷贝运算符之中)
  3. 析构函数不能是删除的
  4. 如果一个类有删除的或不可访问的析构函数,那么其默认和拷贝构造函数会定义为删除的。
  5. 如果一个类有const或引用成员,则不能使用合成的拷贝赋值操作。

先调用的构造函数,最后调用析构函数。

这是程序设计的逻辑,先构造的可能为后构造的提供依赖。

转换构造函数(一个参数的构造函数)

调用拷贝赋值运算符,会生成临时匿名对象(调用移动构造函数)

拷贝构造函数 和 拷贝赋值运算符

  • 注意分析拷贝构造和拷贝赋值运算符!
  • 构造对象时,调用的是拷贝构造函数;在给已有对象赋值时,调用拷贝赋值运算符
  • 拷贝构造一定要const修饰,为了兼容处理两种场景(传入的被拷贝对象是const和非const类型)
  • 提供右值引用的复制构造函数的好处,就是 可以节省很多复制对象的开销 ,调用右值引用的复制构造函数,我们可以简单地认为对象直接跑过去了,并没有发生任何复制。

深拷贝和浅拷贝

C++实现的深拷贝

new和malloc的区别(原地构造?实现深拷贝用的还挺多)

  • new构造的时候会调用构造函数并且会初始化
  • delete会自动调用析构函数

属性与方法

  • 成员属性和方法(对象)
  • 类属性和方法(类)
  • 类属性:所有对象访问的都一样,不能使用this指针。定义的时候在前面加上static,全局区?
  • 注意辨别:类属性的声明和定义。
  • 可以通过域限定符对象去调用
  • const类型的方法与mutable关键字:const声明是为了说明不修改对象内部任何成员属性,主要是给const类型对象使用;mutable就是为了在上述const的修饰下,某些成员属性仍然可以被修改。

const方法

  • 不对当前属性进行更改。
  • const对象不能调用非const对象的方法。
  • const类型方法不能调用非const类型方法
  • const声明是为了说明不修改对象内部任何成员属性,主要是给const类型对象使用

对象与引用

  • 引用在定义的时候要绑定

c++中的结构体与类

  • c++中只是保留了struct关键字,与class的底层实现基本一样。唯一不同的就是默认访问权限不同,struct默认public,class默认private。
  • 为什么要保留struct关键字?
  • 为什么默认访问权限public?
  • 考虑到语言推广成本,收获c用户群体

对象初始化

  • 开辟对象数据区、匹配构造函数、完成构造
  • 开辟对象数据区、匹配拷贝构造函数、完成构造

运算符重载

一个作用域内几个函数名字相同,但是参数列表不同,称为函数重载。(与返回值无关)
重载运算符:注意思考返回引用的作用是什么?
一般需要实现连等操作,对象

类外重载运算符

  • 普通运算符重载
  • 重载cout的左移运算符

类内重载运算符

  • 普通运算符重载
  • 重载下列三个符号,得到的特殊命名:
[] 数组对象
() 函数对象
-> 指针对象

其他重载的知识点

不能重载的5个运算符(五大创世宝石)

  • .成员引用运算符
  • .*成员指针引用运算符
  • sizeof运算符
  • ?:唯一的三目运算符
  • ::作用域操作符

只能在类内部重载的4个运算符

  • ()函数括号运算符(函数对象)
  • []数组方括号运算符(数组对象)
  • ->间接引用运算符(指针对象)
  • =赋值运算符

RVO/NRVO返回值优化

  • 正常情况的拷贝操作
  • 一次优化的情况
  • 二次优化的情况
  • g++使用 -nfo-elide-constructors 选项关掉返回值优化

返回值优化(RVO)

在这里插入图片描述

  • 逻辑是优化掉,临时匿名对象!
  • 如果没有这个操作,可能是被编译器优化掉了
  • 大多数编译器会对拷贝构造函数进行返回值优化(替换this指针完成)

练习

std::shared_ptr -----> c++实现简单的智能指针

## 继承

子类将父类所有的属性和方法都继承过来

继承-子类的访问权限

在这里插入图片描述
继承权限不影响子类对父类内容的访问?

继承-对外的访问权限

在这里插入图片描述
继承权限影响类外部访问子类内部继承自父类这些属性的访问权限

  • 在具有继承关系的情况下,当咱们子类实现拷贝构造的时候,需要显式性的去调用父类的构造函数
  • 怎么实现子类的重载赋值运算符呢?

菱形继承

  • 内存会重复?
  • 实际情况下,一个真实基类吗,多个功能性基类,
  • 思考与虚继承的配合。

代码不是能用就行!命名非常重要!
如果需要多个不允许拷贝的类,不推荐直接把各个拷贝函数delete掉,建议各个类继承自一个不允许拷贝的类!

#include<iostream>
using namespace std;

class A {
public:
    A()=default;
    A(const A&)=delete;
    A &operator=(const A&)=delete;
};

class B : public A{
    
};

class C : public B{

};


int main() {

    A a,c;

    return 0;
}

## 多态

C++多态例子:virtual、override、final

  • 普通成员跟着类走,在编译期就已经确定了
  • 虚函数跟着对象走,在运行期确定,在编译期不能确定
  • virtualoverridefinal的关键字的配合,控制是否覆盖父类同名方法,override是为了规范代码的语义,不是必须要写的,final禁止子类中出现与父类同名的函数,终止虚函数
  • final:禁止类继承、禁止虚函数重载

override

  • 为什么要有这个关键字?
  • final关键字的应用场景:父类中有一个方法需要在所有地方都表示的一模一样,不想让子类去修改,就把相关函数标记成final

虚函数为什么跟着对象走?

对象的前8个字节,存储的是一个虚函数表的地址,可以通过修改对象头8个字节的地址来修改虚函数表,从而调用不同的虚函数。如 ((void **)(&b))[0] = ((void **)(&c))[0]就是把b的虚函数表修改成c的虚函数表了,调用的是c的方法

  • this指针其实是底层隐藏的函数

成员方法指针?

  • 函数指针
  • c++ 成员函数指针
  • 普通方法为什么不能存储成员方法地址?因为它有默认的this指针

dynamic_cast动态类型转换

  • 将基类的地址转换成其派生类的地址
  • dynamic_cast是运行期的行为
  • dynamic_cast与虚函数表是有关系的

虚析构函数

普通成员方法是跟着类走的,为了让其跟着对象走,就要设置成virtual

  • 规范析构顺序,使用虚析构函数

纯虚函数(抽象类)

# 知识强归纳

auto关键字

  • 在c语言中,用来说明局部的自动变量,当前变量的生存周期是编译器自动决定的,定义它的时候就产生了,出作用域的时候就回收了。(和局部变量一样)
  • c++11中, 使用auto方便遍历容器,在编译期就确定了,与sv中的foreach是一样的功能,typeid(x).name()可以查看属性;
  • auto不能作为函数参数;不能作为模板参数;不能定义数组;不能用于非静态成员属性;

constexpr编译期常量

  • const 运行期常量

  • 与const关键字的区别:一个是编译期常量,一个是运行期常量。

  • 修饰全局函数、修饰普通函数、修饰构造函数
    在这里插入图片描述
    在这里插入图片描述

  • 在模板中也会与decltype来搭配使用

NULL和nullptr

  • NULL在C与C++中的区别
  • C++中的NULL所带来的的歧义:func(int),func(int *)
  • nullptr是空指针更准确、无歧义的语义表达

指针和引用的区别

  • 指针指向内存中的某个对象,而引用绑定到内存中的某个对象,它们都实现了对其他对象的间接访问,二者的区别主要有两方面:
  • 指针本身就是一个对象,它允许对指针赋值和拷贝,而且在指针的生命周期内它可以指向不同的对象;引用不是一个对象,无法令引用重新绑定到另外一个对象
  • 指针无需在定义时赋初值,和其他内置类型一样,在块作用域内定义的指针如果没有被初始化,也将拥有一个不确定的值;引用则必须在定义时赋初值。

右值引用

  1. 左值与右值
    到了代码的下一行,是否可以通过单一变量访问到值:能访问到就是左值,不能就是右值、(判断不严谨,但是可以用)
  2. 左值引用与右值引用
  3. 非const变量绑定引用优先顺序
  4. const变量绑定优先顺序
  • 左值引用,使用T&,只能绑定左值
  • 右值引用,使用T&&,只能绑定右值
  • 常量左值,使用const T&,既可以绑定左值又可以绑定右值
  • 已命名的右值引用,编译器会认为是个左值
  • 编译器有返回值优化,但不要过于依赖

引用转换

forward与move:强制调用想调用的函数类型

  • 完美转换,主要在模板当中使用,
  • forward就是一个类型转换,
  • move主要把一个左值或者右值转换成一个右值

左绑左,右绑右;不管咋,先看const;const Type t可以绑定所有类型数据

move constructor 移动构造

  • 把一个右值转换成左值
  • 在战场上,死兵的武器直接抢过来使用

# 三、泛型编程:模板

模板在编译期完成完整的运算,其他的一般在运行期。
程序 = 算法 + 数据结构

数据结构: 能够存储任意类型
算法:能够存储操作存储任意类型数据的数据结构

面向过程编程:用模板实现函数过程
面向对象编程:用模板实现类

模板文件在编译阶段就干掉了,必须写在头文件中!

返回值后置

引用类型推导

T&&传入引用类型,
引用折叠:出现偶数个&叫右值引用,出现奇数个叫左值引用。

特化模板

设计一个可以解析变参列表的工具,例如使用变参列表中的第二个类型定义一个变量。

# 其他内容

异常

C++线程简单使用

std::thread

bind简单使用

function<void(int)>

线程池、线程安全的日志打印功能

设计模式:单例模式

智能指针模板

lambda

# 扩展课程

为什么for auto可以访问vector?

  1. vector和for auto都是自带的?(不是这样的)
  2. vector实现了特殊的元素,使得支持for auto可以实现。

后记 :低调思考

  • 企业不欠你的,不要带有学生思维意识,一年四季都有实习,随时准备一份简历
  • 大量的投递简历,填写自己真实的简历,都能获得大量的面试机会,撩要大胆,“渣男”思维
  • 自信是需要建立的,从小公司开始积累经验和面试机会,要有责任感,不要逃违约金。
  • 简历:留联系方式、在学校后面可以做特殊标记,减少废话(自我评价没用、年龄不要写、党员、籍贯没用)、个人技能一定要展开来写;多写客观因素、少主观因素;如果要证明c++这一块,可以写实现过stl中的某些东西,掌握了哪些知识点,算法数据结构、网络编程:掌握哪些数据结构,掌握哪些 排序算法,高级数据结构掌握到什么程度,比如了解红黑树呢还是实现红黑树?
  • 实现过linux下的某些命令,比如ls、cp、cat、find,自己实现pwd等命令;
  • 项目经历,是为了证明你在某些技能上是擅长的,不要写的浮夸
  • 要证明自己是想做技术的,对技术是热爱的,态度大于能力,以严谨的态度面对技术
  • 大公司造轮子,小公司用轮子
  • 面经的知识在于背,不在于会,不要只盯于c++开发:后端、后端、服务器、c++软件开发。
  • 0
    点赞
  • 2
    收藏
  • 打赏
    打赏
  • 0
    评论
©️2022 CSDN 皮肤主题:黑客帝国 设计师:我叫白小胖 返回首页
评论

打赏作者

杰之行

你的鼓励将是我创作的最大动力

¥2 ¥4 ¥6 ¥10 ¥20
输入1-500的整数
余额支付 (余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值