超全面的C++知识点概括

    科技2022-07-17  125

    C++是对C的补充和扩充,C是C++的一个子集 C 面向过程 用函数解决问题 C++ 面向对象 用成员方法解决问题 C++在兼顾了C执行效率高的优点下提高了编程效率 C++之父——本贾尼.斯特劳斯特朗普 C++ 的文件后缀名可以是 .cpp .cxx .c++ .cc .C 编译C++程序 g++ 或 gcc -lstdc++

    1.名字空间 namespace (1)概念 C++对于变量、类型、函数在逻辑上的一个划分,一个名字空间相当于一个作用域 (2)使用 声明定义一个namespace namespace name{

    }; ①using namespace name; ②Using name :: 标识符 ③name :: 标识符 (3)匿名名字空间 C++对于没有名字空间限制的标识符默认归入到匿名名字空间 如果在程序中需要指明访问,可以用 ::标识符 来制定访问匿名名字空间标识符

    2.C++的类型 (1)char short int long float double都与C一样 (2)结构体 ①C : sizeof(空结构体)=0 ②C++ : sizeof(空结构体)=1 ③C++中,当struct类型定义后,声明或定义变量时,可以省略struct关键字 (3)联合 C++支持匿名联合,即不需要union name{};中的name (4)枚举 ①是一种独立的数据类型 ②不能把整数变量或者整数数值赋值给枚举变量但可以把枚举变量赋值给整数变量 (5)布尔 C++中有bool类型,不再是0和1,非”零”即为真 “零”: 0 0.0 ‘\0’ NULL “” (6)运算符别名 && and || or ! not [ <: ] :> { <% } %> &= and_eq & bitand | bitor ~ compl != not_eq != 0r_eq ^ xor ^= xor_eq 3.C++的函数 C++中函数没有隐式声明,在调用前必须先声明 C++函数中没有形参,不能传递实参 C语言中struct内不能声明函数,但C++中可以,声明的函数作为成员方法可以直接访问成员变量 (1)重载 同一作用域下,函数名相同,参数列表不同即构成重载 参数列表不同: 参数个数不同 参数类型不同 (2)缺省值 ①在声明函数时参数可以设置默认值,调用函数时可传可不传 ②缺省参数靠右原则 ③当声明和实现分离时,缺省参数只能放在声明中,实现中不能带缺省参数 (3)哑元 ①只有类型,没有形参名就叫哑元 ②只关心参数类型,不关心参数的值 ③用于在运算符重载中区分前+±-和后+±- (4)内联函数 inline ①修饰一个函数,直接用函数的二进制指令替换调用指令 ②运行效率高,生成的程序更大,一般简单的函数可以内联,复杂的没必要

    4.动态内存 new/new[] delete/delete[] 都是操作符,不是函数,底层调用malloc/calloc (1)申请和释放单个变量的动态内存 以int类型为例: 申请: int* a=new int; int* b=new int(10); 释放: delete a; (2)申请数组形式的动态内存 以int为例: 申请: int* a=new int[5]; int* a=new int[5]{1,2,3,4,5}; 释放: delete[] a; (3)malloc与new的区别 ①malloc是函数,new是操作符 ②malloc不会调用构造函数,new会自动调用类构造函数 ③malloc可以用于数组动态内存的申请,new不行,new[]才行 ④malloc需要给定申请内存大小的字节数,new会根据类型自己计算 ⑤malloc返回void*,new返回指定类型地址 ⑥malloc失败返回NULL,new出错抛出异常 5.引用 int& b=a (1)引用即变量别名,且必须初始化,引用变量定义后,引用变量和目标变量本质上是同一个变量 (2)引用变量一旦定义,就不能更改引用对象 (3)引用的意义 ①用于函数传递参数(使用引用也可以在函数中更改实参的值) ②返回一个结果作为左值 ③提高传参效率,节省内存 (4)引用与指针的区别: ①引用必须初始化,指针可以不初始化(野指针) ②引用目标不能为控,指针指向可以为空 ③引用一旦定义,就不能改变引用目标,指针任何时候都可以修改指向目标 ④可以声明指针的引用,但不能声明引用的指针 ⑤可以声明指针的指针,但不能声明引用的引用 ⑥可以声明指针数组,但不能声明引用数组 ⑦可以声明数组的引用,也可以声明数组的指针 6.类型转换 (1)隐式类型转换 可以自动转换,不会有警告和错误 (2)强制类型转换(不建议使用) 目标变量=(目标类型)源对象 (3)显示类型转换 ①静态类型转换 static_cast<目标类型>源对象 ②去常类型转换 const_cast<目标类型>源对象 ③重解释类型转换 reinterpret_cast<目标类型>源对象 ④动态类型转换 dynamic_cast<目标类型>源对象 7.字符串 (1)C++中支持C风格的字符串 (2)C++中提供了string类型

    8.面向对象编程 (1)类 class 类名{ 成员属性; 成员方法; }; (2)访问控制属性 ①public 公开的,任何地方都能访问 ②protected 保护的,只能在类或子类内部访问 ③Private 私有的,只能在本类内部访问 (3)class与struct的区别 ①class中默认访问控制属性是private ②struct中默认访问控制属性是public (4)构造方法 用于实例化一个对象 A.构造方法没有返回值类型,也不能是void B.构造方法名必须和类名一模一样 C.当一个类没有实现构造方法时,会默认提供一个无参构造方法,当程序运自己 有实现构造方法时,不提供无参构造方法 D.构造方法可以重载 (5)this ①每一个类成员方法中都隐含一个this指针 ②this指针指向正在调用该方法的对象 (6)初始化列表 ①在实现构造方法时,可以用一个初始化列表对成员属性进行初始化 ②对于长属性成员和引用成员,不许在初始化列表中初始化 ③初始化列表的执行顺序至于成员变量的声明顺序有关,与初始化列表的语句先后顺序无关 (7)常对象和常函数 ①被const关键字修饰的成员属性和成员方法称为常对象和常函数 ②常对象只能调用常函数,不能调用普通成员函数 ③常函数和非常函数可以构成重载 ④形参列表中引用和指针的常属性不同会构成重载,而普通变量会变成重定义 (8)mutable ①用于修饰成员属性 ②在常函数中,不能修改普通成员属性,但可以修改被mutable修饰的成员属性 (9)static ①static修饰的属性称为类属性或静态属性 ②静态属性需要在类外声明,且不能在初始化列表中初始化 类型 类名::静态属性=x; ③静态属性属于类,可以用 类名::静态属性 来访问,也能通过对象.静态属性访问 ④静态属性属于类,所有对象共享一份

    ⑤static修饰的方法称为静态方法,因为没有隐含的this指针,不能直接访问成员属性,不能调用成员方法,但可以直接访问静态属性 (10)explicit class A{ A(B b){} }; 在任何一个类中,如果实现了单参构造函数 A(B b){} 那么,B类型的对象都可以借助单参构造函数成为A类型的对象 构造函数可以用explicit修饰 防止单参构造函数的隐式调用 (11)析构函数 当一个对象消亡或者delete时,会调用析构函数 如果一个对象在构造时new了动态内存,则需要手动实现析构函数 (12)拷贝构造 ①用一个已经存在的对象来构造一个心得同类型的对象 ②类名(const 类名& a){} ③默认是按字节拷贝,新旧对象一模一样,实现的是浅拷贝 ④深拷贝:拷贝指针所指向内存地址中的数据,而不是拷贝指针的值本身 (13)拷贝赋值构造 ①如果没有实现拷贝复制构造,编译器会自动提供 ②默认是浅拷贝 ③需要实现深拷贝则需要自己实现,实际上是重载”=”运算符

    9.string C++里的string类型实际上是一个类,可以自己实现string类型 class string{ private: char *s; public: string(const char *str)😒(strcpy(new char[strNULL?1:strlen(str)+1],strNULL?””:str)){} ~string(){ delete[] s; } string(const string& str)😒(strcpy(new char[strlen(str.s)+1],str.s)){} string& operator=(const string& str){ if(this!=&str){ string tmp(str); swap(tmp.s,s); } return this; } };

    10.成员属性指针和成员函数指针 (1)定义成员属性指针变量 属性类型 类名::*指针变量;

    初始化 or 赋值 属性类型 类名::*指针变量 = &类名::属性名; //不是一个真实的地址 指针变量 = &类名::属性名; 成员指针直接解引用 对象 对象.*成员指针 成员指针间接解引用 对象的指针 对象的指针->*成员指针 (2)定义成员函数指针变量 成员函数返回值 (类名::*指针)(成员函数形参列表); 初始化 or 赋值 成员函数返回值 (类名::*指针)(成员函数形参列表) = &类名::成员函数名; 指针 = &类名::成员函数名; 成员函数指针直接解引用 对象 (对象.*成员函数指针)(实参列表); 成员函数指针间接解引用 对象的指针 (对象的指针->*成员函数指针)(实参列表);

    11.友元类 (1)在一个类里面,可以声明另外的类为该类的友元类 (2)在友元类中,可以访问该类对象的私有属性和私有函数 (3)可以声明一个全局函数为该类的友元函数,则在友元函数中可以访问该类的私有属性和方法 (4)友元的声明是单向的 12.运算符重载 (1)本质上是一个函数 返回值类型 operatorX(形参列表){} X为运算符,例: + - * / = (2)对运算符进行分类 ①单目运算符 +(正) -(负) *(取值) &(取地址) !(逻辑非) ~(按位取反) ++ – ②双目运算符 两个操作数对象的类型不一样 ③三目运算符 三目运算符不可以重载

    (3)重载自增减运算符 ①用哑元来区分前后++ -- ②后++ 类名& operator++(){} ③前++ Const 类名& operator++(int){} (4)重载()运算符 重载了()运算符的类的对象称为函数对象,因为这种类型的对象可以像函数一样调用 (5)重载类型运算符 class A{ Operator B(){ return B类型对象; } }; 此时,A类型对象都可以自动转换成B类型的对象 A类型对象怎么自动转换成B类型的对象? 1)在B类中添加A类型的单参构造 2)在A类中重载B类型的类型运算符 (6)一元谓词 返回值类型为bool的仿函数称为谓词 如果operator()接受一个参数,叫做一元谓词

    13.继承 (1)用一个类去继承另一个类,被继承的类称为父类(基类),继承父类的类称为子类 (2)子类拥有父类的属性和方法 (3)继承的意义是提高代码复用率和实现功能的扩展 (4)语法规则: ①class S:继承方式1 F1,继承方式2 F2,…{} ②继承方式: 1)public 2)protected 3)private (5)访问控制属性 基类 子类 其他地方 public OK OK OK protected OK OK NO private OK NO NO

    继承方式会改变基类属性在子类中的访问控制属性 继承方式 共有属性 受保护属性 私有属性 public public protected x protected protected protected x private private private x 保护继承和私有继承的意义在于不会让父类公开的属性和方法通过子类扩散出去 (6)子类的构造函数 ①按照继承顺序依次调用父类的构造函数(默认无参构造) ②按照声明顺序依次调用类类型成员的构造函数(默认无参构造) ③执行本类的构造函数 如果一个类继承的父类没有无参构造,那么这个子类一定要实现构造函数 (7)子类的析构函数 ①默认的子类析构函数会调用父类的析构函数 ②析构函数的调用顺序与调用构造函数的顺序相反 ③手动实现子类的析构函数依旧会调用父类的析构函数 (8)子类的拷贝构造和拷贝赋值 ①默认调用父类的拷贝构造 ②如果自己实现子类的拷贝构造,需要在初始化列表中调用父类的拷贝构造 ③默认调用父类的拷贝赋值 ④如果自己实现子类的拷贝赋值,则不会调用父类的拷贝赋值 需要手动调用: static_cast<父类*>(this)->operator=(实参); 父类::opeartor=(实参); (9)父类和子类的联系 任何一个子类对象中,都有一个父类子对象 (10)父类指针和子类指针 ①父类指针可以指向子类对象 ②子类指针可以隐式转换成父类类型指针 ③父类指针不可以隐式转换成子类类型指针 可以通过static_cast<>进行显示类型转换,可以得到原来子类对象的首地址 通过reinterpret_cast<>重解释显示转换不能得到原来子类对象的首地址 static_cast<>转换不会进行校验,肯定可以成功,但很危险 (11)父类引用和子类引用 ①父类引用可以引用子类对象 实际引用的是子类对象中的父类子对象 ②子类引用不能引用父类对象 但可以用静态类型转换 (12)虚继承 对于可能出现的一个子类继承了多个基类,而所继承的多个基类中又有继承自同一个基类而产生的数据不统一的问题,可以使用虚继承,来使在最终的子类中只拥有一份共同基类的成员。

    14.虚函数 如果一个成员函数声明为virtual,那么该函数就是虚函数,在子类中可以重写该函数 虚函数一般用于多态 15.重写(覆盖) (1)重写:子类重写父类的虚函数 ①父类的函数必须是虚函数 ②子类函数名必须和父类函数名一致 ③函数的参数及常属性必须一致 1)普通类型的参数,常属性可以不一致 2)参数类型是类类型的指针或引用,常属性必须一致 ④返回值如果是基本数据类型和类类型(不是指针和引用),必须一致 如果返回值类型是类类型的引用或指针,则子类重写的版本返回值可以是父类版本返回值类型的子类类型引用或指针 ⑤子类重写版本的异常说明不能比父类声明抛出更多异常 ⑥子类重写版本的访问控制属性不受父类访问控制属性的影响 (2)重载:在同一作用于下,函数名相同,参数列表不同,与返回值类型无关,则构成重载 (3)隐藏:子类隐藏父类同名的属性和方法,方法名相同 ①如果有virtual关键字,如果不重写,即构成隐藏 ②如果没有virtual关键字,如果不重载,则隐藏 16.拥有虚函数的类 (1)如果一个类拥有虚函数,那么这个类的内存宽度会增加一个指针的大小,用于指向这个类的虚函数表,这个指针称为虚表指针 (2)虚函数表是一个指针数组,数组中的每一项都是一个虚函数的地址 (3)如果一个类继承的基类有虚函数,那么该类也会继承基类的虚函数表生成子类自己的,如果子类有重写基类的虚函数,则该子类虚函数表中对应的值(虚函数的地址)将会被覆盖 (4)虚表指针,每一个继承了有虚函数的基类的子类都有,同一个类的对像共享该指针 17.多态 多态=虚函数+指针/引用 子类重写了基类的虚函数 用父类的引用引用子类对象或用父类指针指向子类对象 去调用父类中的虚函数,此时调用的将不再是父类的虚函数,而是调用子类重写的版本,这个现象就称为多态 重写以后,调用的函数不在决定于指针和引用本身的类型,而是却决于调用对象的类型 18.静态绑定/动态绑定 (1)静态绑定 调用重载方法时,在编译阶段根据实参类型和个数绑定对应的方法 (2)动态绑定 在编译阶段无法确定调用哪个方法,只用当运行时才能确定 19.dynamic_cast<> 用于多态父子指针或者引用之间的转换 一定是多态,不然编译报错 20.typeid (1)求类型和对象的类型信息 (2)对于普通的类型和对象,就是其本身类型 (3)对于多态,typeid(父类指针)或者typeid(父类引用),求得的是目标真实类型 (4)typeifno.name() 支持 == != 比较 21.虚析构 把析构函数设置为virtual,那么在用基类指针new子类对象时,delete基类指针,调用的是子类的析构函数,而子类析构函数会默认调用父类的析构函数,这样就避免了内存泄漏的问题 即使不使用多态,也推荐把析构函数设置为virtual,以避免内存泄漏

    22.抽象类 (1)有纯虚函数的类称为抽象类 (2)纯虚函数即虚函数没有函数体 virtual func()=0 称func为纯虚函数 (3)抽象类不能实例化对象 (4)继承抽象类就会继承纯虚函数,如果子类不重写纯虚函数,那么子类也是抽象类 (5)如果一个类只有纯虚函数,那么这个类称为纯抽象类 (6)抽象类的意义: ①提供一个公共的类型 ②虚函数提供一个统一的接口,子类根据自身需求重写 ③基类的实现没有意义

    关于虚函数: 函数类型 是否可以是虚函数 原因 内联函数 不可以 原理和虚函数正好相反 全局函数 不可以 虚函数一般用于父子类型之间 以成员方式重载的运算符 可以 可以重写 构造函数 不可以 编译时会报错 析构函数 可以 建议这么做,避免内存泄漏 静态成员函数 不可以 静态函数没有多态

    23.异常 (1)#include try{ 可能产生异常的语句 }catch(异常类型& e){ 处理方式 } 根据异常类型来捕获,子类异常先于基类异常catch (2)抛出异常用throw (3)定义异常类型,一般继承 exception (4)当程序发生异常之后,直接中断代码,抛出一个异常对象到catch分支去进行类型匹配,如果匹配到了相应的异常类型,则进行对应的catch分支执行代码,否则继续往外抛,知道被捕获或者默认处理(终止程序,输出异常信息) 如果捕获之后成功处理,代码接着try-catch分支往后继续正常运行 (5)函数可以有异常说明 返回值类型 函数名(形参列表)throw(可能抛出的异常类型){} A.如果一个函数没有异常说明,则表明该函数可以跑出任意异常 B.如果在一个函数内抛出不在异常说明里的异常类型,则try-catch时无法catch 该异常类型 C.函数的异常说明是为了异常时方便去写catch捕获的异常类型

    24.函数模板 (1)语法规则 template<typename T,…> 函数 (2)给类型之后实例化,生成对应类型的函数 (3)对于某些特殊类型,函数模板不一定适用,需要函数模板特化(全特化) 25.类模板 (1)语法规则 template class cls_name{}; (2)类模板全特化 把整个类所有成员和方法都重写一遍 (3)类模板成员特化 只重写某些成员方法,不需要特化处理的不重写 template<> ret_type cls_name<特化的类型>::mem_name(形参列表){} (4)类模板偏特化 针对类型参数相同或者针对类型的指针和数组进行局部特化 在实例化模板时,需要避免产生歧义 (5)模板的类型可以有默认类型,模板可以有非类型参数 templat (6)在模板定义中,typename和class可以相互替代

    26.STL(标准模板库) (1)十大容器 ①线性容器 1)vector:向量 2)list: 列表 3)deque:双端队列 ②容器适配器 1)queue: 队列 2)Stack: 堆栈 3)properity_queue:优先队列 ③关联容器 1)map: 映射 2)multi_map:多重映射 3)set: 集合 4)multi_set: 多重集合 (2)共同特点 ①支持自动扩容 ②支持深拷贝 拷贝赋值和拷贝复制 ③支持 == != 比较 如果认为两个容器相等,则容器容量相等,且相同位置的元素也相等 ④往容器中存储元素时,是存储元素的副本(拷贝构造) ⑤容器如果要支持sort自定义类型需要重载<或者提供比较的函数对象

    Processed: 0.010, SQL: 8