C++初级杂记
(申明:仅为C++一些知识点的零散摘要和浅析,不涉及具体代码和很深入的剖析)
C++:
抽象、封装、继承、多态(继承、多态:面向对象核心)
C++默认方法(6个)
构造函数
拷贝构造函数
赋值语句(=的重载)
析构函数
普通对象取地址(&的重载)
const对象取地址(const对象&的重载)
1 类的定义与对象的创建
0.作用域限定符:public;protected;private
1.作用域解析运算符:::
2.方法的外部实现:返回值 类名::函数名(参数){}
3.成员访问运算符:.(只有公有成员才能在对象外访问)
2 this指针
//方法中区分 当前是在操作哪个对象的数据
实质为当前对象的地址
编译过程
1.解析类名
2.识别数据成员
3.识别并改写函数(函数参数 插入(当前类类型 *const) this指针)
const指针
int a = 10;
const int *p; //*p为常量,即指针p所指向的对象不能通过p修改(const在*左边)
int * const p; //p为常量,即指针p所指向的对象通过p修改(const在*右边)
const int * const p;
//*p和p都为常量,即不能通过指针p修改其所指向的对象;且指针p所指向的对象不能改变(只能读取其指向对象的值)
3 构造函数和析构函数
3.1 构造函数的定义和使用
1.函数名与类名一致
2.无函数返回类型说明
3.仅在对象被创建时自动调用一次
4.可以重载,调用时根据参数列表选择其中一个
5.类中类外均可定义
6.如果没有构造函数,编译器自动生成一个缺省构造函数;如果有构造函数,则不会自动生成!
构造函数无参或参数均有缺省值,编译器都认为时缺省构造函数;缺省构造函数只能有一个
对象使用无参构造:
Test t; //Test t();错误:被认为是函数声明
构造函数的3个作用
1.构造对象
2.初始化对象
3.类型转换(隐式) //产生临时对象!
explicit关键字:显式---修饰构造函数,禁止隐式类型转换
-----------------------------------------------------------------------------
3.2 析构函数的定义
一个对象的生命周期结束时,C++也自动调用一个函数注销该对象并进行善后工作,
这个特殊的成员函数即析构函数(destructor):
1.构函数名与类名相同,但在前面加上字符‘~’,如
~CGoods()。
2.析构函数无函数返回类型;不带任何参数。
3.一个类有且只有一个析构函数,这与构造函数不同。析构函数可以缺省。
4.对象注销时,系统自动调用析构函数。
4 引用和拷贝构造函数
4.1 引用(又称别名)
变量、指针、数组、const变量 引用
4.2 拷贝构造函数
函数名与类名一致,参数为该类类型的const引用
调用时机:(本质都是用类对同类对象初始化)
1.用类对同类对象初始化时
2.函数参数为类时(无名临时对象)
3.函数返回值为类时(无名临时对象)
4.3 赋值语句
5.函数的调用优化
1.返回无名临时对象:
return Test(value); //没有类名;不同于Test xxx(value);
2.引用传值
3.引用返回(注意返回对象的 生存周期)
6 深拷贝与浅拷贝
类中的数据成员包含指针;一般需要重新编写:
拷贝构造函数 和 赋值语句
默认的函数不能完成工作;会产生:浅拷贝和深拷贝的后果
默认拷贝构造函数
浅拷贝:只拷贝了指针的指向
深拷贝:重新分配空间,并拷贝指针指向
默认赋值语句
浅赋值:只对指针进行简单赋值
深赋值:重新申请空间,并拷贝指针指向
1.是否自己给自己赋值
2.指针是否已有指向(有则释放,指向NULL)
3.重新申请空间;拷贝内容
4.返回this引用
7 运算符的重载
定义运算符重载函数的一般格式:
返回值类型 类名::operator重载的运算符(参数表)
{……}
operator是关键字,它与重载的运算符一起构成函数名。
1.运算符重载函数的函数名必须为关键字operator加一个合法的运算符。在调用该函数时,将右操作数作为函数的实参。
2.当用类的成员函数实现运算符的重载时,运算符重载函数的参数(当为双目运算符时)为一个或(当为单目运算符时)没有。
运算符的左操作数一定是对象,因为重载的运算符是该对象的成员函数,而右操作数是该函数的参数。
3.单目运算符“++”和“--”存在前置与后置问题。
前置“++”格式为:
返回类型 类名::operator++(){……}
而后置“++”格式为:
返回类型 类名::operator++(int){……}
后置“++”中的参数int仅用作区分,并无实际意义,可以给一个变量名,也可以不给变量名。
8 友元
friend
友元函数和友元类
不是类的成员,能访问类的私用部分
不受访问限定符限制
9 静态成员
static
静态方法 静态数据(不含this指针)
静态数据
在类外初始化;可直接通过类名访问
一个类的所有对象所公有;不属于任何具体的对象
静态方法
静态方法只能调用静态数据和方法;普通方法可调用静态方法(含const this指针)
---------------------------------------------
const
常方法:不能对类的数据成员做修改
void print()const ==>void print(const Test * const this)
10 模板
1-7章
1.函数模板
编译器会根据模板 通过实参演绎或指定的参数类型生成相应类型的 模板函数
模板声明
template <typename Type1,typename Type2>
2.类模板
所有成员函数都是模板函数,在类外实现以模板声明开头;必须指明类型标识
11 动态内存分配
静态内存分配 栈区
动态内存分配 堆区
new delete
1.无需进行地址类型的转换;
2.无需判断是否返回地址是否为 NULL ;
3.数组类型需指定[size]
4.申请同时可以初始化
new运算符: 1.malloc 2.Constructor
delete运算符: 1.destroy 2.free
new 的三种形式
内存泄漏检测#include <vld.h>
1.new operator //new操作符:1.operator new(只负责开辟空间) 2.Constructor
2.operator new //操作符new:只负责开辟空间
3.placement new //定位new:可以显式调用构造函数
new(p,偏移量)类型(初始值)
new的重载
返回类型:void* 参数类型:size_t //unsigned int
1.new operator
void* operator new(size_t sz){}
3.placement new
void* operator new(size_t sz,int *d,int pos){}
12.继承与多态
1.继承访问属性
继承属性 public protected private(默认 private)
含义:子类的 【继承类型】部分 继承 父类除构造和析构函数之外的所有内容
1.子类会继承父类的 除构造和析构函数之外的所有内容
2.子类必须通过共有方法 访问父类的私有部分(private)
3.子类可直接 访问父类的保护部分(protected)
对象只能直接访问共有部分(public)
2.派生类的析构函数和构造函数
(1)派生类的构造函数的定义形式为:
派生类名::派生类名(参数总表):基类名1(参数表1)《,基类名2(参数表2),……,基类名n(参数表n)》,
《成员对象名1(成员对象参数表1),……,成员对象名m(成员对象参数表m)》{
……//派生类新增成员的初始化;
} //所列出的成员对象名全部为新增成员对象的名字
派生类构造函数各部分的执行次序为:
1.调用基类构造函数,按它们在派生类定义的先后顺序,顺序调用。
2.调用成员对象的构造函数,按它们在类定义中声明的先后顺序,顺序调用。
3.派生类的构造函数体中的操作。
*在派生类构造函数中,只要基类不是使用缺省构造函数都要显式给出基类名和参数表。
(2)析构函数的功能是作善后工作。
析构函数各部分执行次序与构造函数相反,首先对派生类新增一般成员析构,
然后对新增对象成员析构,最后对基类成员析构。
3.钻石继承和虚基类
virtual(只用于类的成员函数的申明)
虚基类--->虚拟继承--->钻石继承
1.不同父类有相同名称的数据成员,用访问限定符::区分
2.不同父类有相同名称的数据成员,但只继承一份数据:使用虚拟继承
3.派生类对象创建:构造顺序:虚基类--->非虚基类--->成员对象--->派生类本身
4.同名隐藏和赋值兼容
同名隐藏:派生类同名函数会隐藏所有的基类同名函数
赋值兼容:(向上转换,派生类对象隐含一个基类对象)
1.派生类的对象可以赋值给基类的对象
2.派生类的对象的地址可以赋给其基类的指针变量
3.派生类对象可以初始化基类的引用
13.多态性与虚函数
(1)虚函数
虚函数是一个类的成员函数,定义格式如下:
virtual 返回类型 函数名(参数表)
当某一个类的一个类成员函数被定义为虚函数,
则由该类派生出来的所有派生类中,该函数始终保持虚函数的特征。
派生类中重新定义虚函数(overriding a virtual function,亦译作超载或覆盖)时,
不必加关键字virtual。
1.派生类中定义虚函数必须与基类中的虚函数同名外,还必须同参数表,
同返回类型。否则被认为是重载,而不是虚函数。
如基类中返回基类指针,派生类中返回派生类指针是允许的,这是一个例外。
2.只有类的成员函数才能说明为虚函数。这是因为虚函数仅适用于有继承关系的类对象。
3.静态成员函数,是所有同一类对象共有,不受限于某个对象,不能作为虚函数。
4.一个类对象的静态和动态类型是相同的,实现动态多态性时,
必须使用基类类型的指针变量或引用,使该指针指向该基类的不同派生类的对象,
并通过该指针指向虚函数,才能实现动态的多态性。
5.内联函数每个对象一个拷贝,无映射关系,不能作为虚函数。
6.析构函数可定义为虚函数,构造函数不能定义虚函数,因为在调用构造函数时对象还没有完成实例化。
在基类中及其派生类中都动态分配的内存空间时,必须把析构函数定义为虚函数,实现撤消对象时的多态性。
7.函数执行速度要稍慢一些。为了实现多态性,每一个派生类中均要保存相应虚函数的入口地址表,
函数的调用机制也是间接实现。所以多态性总是要付出一定代价,但通用性是一个更高的目标。
8.如果定义放在类外,virtual只能加在函数声明前面,不能(再)加在函数定义前面。
正确的定义必须不包括virtual。
(2)多态:离不开 虚拟、继承、使用基类指针和引用
(拓展性:使用相同的接口实现不同的功能)
派生类重写基类的虚方法:同名、同返回类型、同参数列表
调用时条用派生类的虚方法(同名覆盖)
(3)多态的实现原理(*虚表)
__vfptr—>虚函数的入口地址表 vftable
重载:同一个类,函数名相同,参数表不同
隐藏:基类和派生类,派生类同名函数隐藏基类所有函数
覆盖:基类和派生类,派生类覆盖基类同名虚函数,针对多态
(4)纯虚函数 (占位置,通用抽象接口)
定义纯虚函数的一般格式为:
virtual 返回类型 函数名(参数表)=0;
纯虚函数(pure virtual function)是指被标明为不具体实现的虚拟成员函数。
它用于这样的情况:定义一个基类时,会遇到无法定义基类中虚函数的具体实现,
其实现依赖于不同的派生类。
抽象类:含有纯虚函数的基类是不能用来定义对象的。
1.定义纯虚函数时,不定义虚函数的实现部分。纯虚函数不能调用。
2."=0"表明程序员将不定义该函数,函数声明是为派生类保留一个位置。
"=0"本质上是将指向函数体的指针定为NULL。
3.在派生类中必须有重新定义的纯虚函数的函数体,这样的派生类才能用来定义对象。
14.流类体系以及格式控制
(1)格式设置
cout.flags(ios::hex | ios::showbase); //格式设置
(2)流操作子 endl
cout<<hex<<"test"<<endl;
#include <iomanip.h>
cout<<setw(10)<<"test"<<endl;
15。文件的读写
1.文本文件
2.二进制文件
3.文件的随机读写
#include <fstream.h>
(1)创建一个文件流对象
ifstream;ofstream;
(2)打开一个文件
打开模式 ios::out ios::binary ios::in
(3)读写文件
(4)关闭文件
16.文件与对象
对象创建时读取文件数据,初始化对象
对象析构时将数据写入文件,保存数据
17.异常处理
try 尝试运行可能发生异常的代码
throw 抛出异常
catch 捕获异常(按类型捕获;缺省类型(所有类型:catch all)…)
异常规范
自定义异常类
标准异常类
18.标准模板库(STL)
PJ版;SGI版
容器
算法
迭代器
算法->仿函数
容器适配器
空间配置器
类型 template