因为一些特性复杂,很多时候也用不到一些特性,所以忘记了,算是随笔,也当作一个临时查找的手册。没有什么顺序,很杂。
1.构造函数通过函数重载的机制可以有多个(不同的构造函数,参数个数,或者参数类型不同。),但是析构函数只能有一个。当没有在代码中写明构造或析构函数时,编译器会自动生成缺省的构造或析构函数。构造函数和析构函数都无返回值。另外,析构函数必须无参数。没写复制(拷贝)构造函数,编译器也会自动生成缺省的复制构造函数。复制构造函数会生成一个临时隐藏的对象,在调用一个以类对象作为参数的函数和调用一个以类对象作为返回值的函数的时候,复制构造函数都会自动调用。
2.对于C++类中的普通成员变量每个对象(实例)会各自有一份,而静态成员变量一共就一份(因为是放在内存中静态存储区的,属于全局),为所有对象(实例)共享。而且哪怕一个对象都不存在,静态成员变量都是存在的。
3.对于C++类中的普通成员函数必须具体作用于某个对象,而静态成员函数并不作用于具体某个对象。静态成员函数本质上是一种全局函数。
4.总结2,3,因此静态成员并不需要通过对象(实例)就可以访问。例如:
类A::静态成员函数;
类A的对象.静态成员函数;
5.必须在定义类的文件中对静态成员变量进行一次初始化或者说明,不然编译能通过,链接不能通过。例如 int 类A::TotalNum = 0; 其中TotalNum为静态成员变量。
6.在静态成员函数中,不能调用非静态成员函数,也不能访问非静态成员变量。(但是非静态成员函数是能访问静态成员变量的)。
*******************************************************************************************************
1.成员对象:一个类的成员变量是另一个类的对象。
2.包含成员对象的类叫做封闭类。
3.一般对于封闭类,用初始化参数列表来初始化。
4.生成封闭类对象的语句的时候,要明确“对象中的成员对象”要如何进行初始化。
5.当生成封闭类对象的时候,首先会调用其成员对象中的构造函数,然后再调用自身的构造函数。
6.成员对象构造函数的调用顺序:和成员对象在类中的说明顺序一致,与在成员初始化列表中的顺序无关。
7.当封闭类对象消亡时,先调用封闭类的析构函数,再调用成员对象的析构函数。
8.友元:友元函数和友元类。一个类的友元函数可以访问该类的私有成员。将一个类的成员函数(包括构造,析构函数)声明成另一个类的友元。
class B
{
public:
void func();
};
class A
{
friend void B::func();
};
9.A是B的友元类,就说明A的成员函数可以访问B的私有成员。Note:友元类之间的关系不能传递,不能继承。(就是说A是B的友元类,C又是A的友元类,但是不表明C是B的友元)
10.this指针其作用就是指向成员函数所作用的对象。非静态成员函数中可以直接使用this指针来代表指向该函数作用的对象的指针。
11.可以把C++的程序试着翻译成C程序,所以this指针是隐式生成的,并传递到普通成员函数(非静态)中的。例如:
class A{
int i;
public:
void display() { cout << "hello" << endl} //这条语句实际上可以翻译C程序成void display(A* this) {cout << "hello" << endl}
void display2() {cout << i << endl} //这条语句实际上可以翻译成C程序:void dispaly2(A * this) {cout << this ->i << endl }
};
int main(){
A *p = NULL;
p -> display(); //根据以上注释这条语句实际上是不会出错的。翻译成C:display(p);
p->display2();//这条语句由于对象没有被构造和分配内存,是会出错的。翻译成C:display(p);
return 0;
}
12.this指针和静态成员函数:因为静态成员函数并不作用于具体某个对象,因此,静态成员函数真实的参数个数就是程序中写出的参数个数。
13.常量对象,如果不希望某个对象的值被改变,则定义该对象的时候可以在前面加const关键字。
14.常量成员函数,在类的成员函数后面加入const关键字。常量成员函数执行期间不应修改其所作用的对象。因此,在常量成员函数中不能修改成员变量的值(静态成员变量除外),也不能调用同类的非常量成员函数(静态成员函数除外)。
15.常量对象上面不能调用非常量成员函数,因为非常量成员函数很可能修改对象的值。
16.常量成员函数可以重载,就是在一个类中,有两个成员函数,一个后面有const,一个没有。那么叫重载,不叫重复定义。
17,常引用,在前面加const,不能通过常引用修改其引用的变量。可以用来做函数的参数,因为不通过引用传递类参数,会调用复制构造函数,造成开销。一般通过引用。加上常引用的话,被引用的实参就不会被修改了。
********************************************************************************************************************
1.C++用类的一种方式来做数据抽象,运算符重载就是对抽象数据类型能像基本数据类型(int float)能直接使用C++提供的运算符使程序更容易理解和简洁。就是让运算符附有多重含义。
2.运算符重载的实质是函数重载。在程序编译时,会把含有运算符的表达式转换成运算符函数的调用,把运算符的操作数转换成运算符函数的参数(实参)。重载为普通函数时,运算符函数的参数个数等于运算符的目数。(+这个运算符就是双目运算符。)重载为成员函数的时候,参数个数为运算符目数减一。
返回值类型 operator 运算符(形参)
{
}
3.赋值运算符只能被重载为成员函数。
4.如果有个string类,把赋值运算符重载了,就可以string s; s = “hello”;了。但是却不能string s = “hello”;这样。这样会出错,因为赋值语句和初始化语句不一样。所以自然就不会调用赋值运算符函数了。
5.运算符一般被重载为类成员函数,那被重载为友元函数的情况:
class Complex{
double real,img;
public:
Complex(double r, double i):real(r),img(i){};
Complex operator +(double r);
friend Complex operator +(double r, const Complex &c);
};
6.返回为引用才能作为左值。
7.cout是iostream中定义的ostream类的对象,<<这个运算符能作用在cout上面,是因为<<这个运算被重载了。
8.关于<<运算符重载的一个例子:
怎样才能让cout << 5 << "Hello";这个式子成立呢?当然,如果分开来当然就好实现了。operator << (int n){//输出n的代码}和operator << (const char* s){输出s的代码}。
由以上大概思路可以得到 cout << 5这个表达式必须返回cout(也就说必须返回ostream这个类)才能对 << "Hello"产生作用。
ostream & ostream::operator <<(int n)
{
...//输出n的代码
return *this;
}
ostream & ostream::operator <<(const char* s)
{
...//输出s的代码
return *this;
}
PS:一切东西都是函数啊,果然现代计算机编程语言很多都吸收了lambda演算(Lisp就是吸收lambda演算的思想被设计出来的)的精华啊。
9.自加自减运算符分为前置和后置。前置是一元运算符,后置是二元运算符。所以重载有所不同。
10。强制类型转换运算符被重载时,不能写返回值类型。实际上其返回值类型就是强制类型转换运算符的所当前代表的类型。
operator int () {return n;} //int被作为一个类型强制转换运算符被重载
11.C++不允许定义新的运算符,重载之后的运算符也要注意日常习惯,重载以后的运算符不改变该运算符的优先级。::,.,.*,?:,sizeof,这些运算符不能被重载。
重载运算符(),[],->或者赋值运算符=时,重载函数必须声明为类的成员函数。
************************************************************************************
1.继承:如果有一个新类B,与A有相似的特点(指B拥有类A的全部特点),那么就可以把类A作为基类,而把B作为A的一个派生类。(貌似现在很多公司隐式规定不给用多重继承了)
2.派生类对象的内存空间:派生类对象的体积等于基类对象的体积,再加上派生类对象对象自己的成员变量的体积。在派生类对象中包含这基类对象,而且基类对象的存储位置在派生类对象新增的成员变量之前。
3.复合关系和继承关系:继承是“是”的关系,对象B继承于对象A其实是有两个对象的。复合关系是,逻辑上来说,A是B的固有属性或者是组成部分。(点和圆类的关系就是复合关系,当然可以使用继承,但是不合理,因为一个圆不是点。)以下就是复合关系:
class Cpoint
{
double x, y;
}
class Circle
{
double r;
Cpoint center;
}
4.基类派生类同名成员的情况:派生类的成员函数要访问基类的同名成员函数最好加上域作用符。不然访问的是派生类本身的成员函数或变量。
5.基类的private成员:可以被基类的成员函数访问,也可以被基类的友元函数访问。
6.基类的public成员:可以被基类的成员函数,基类的友元函数,派生类的成员函数,派生类的友元函数访问。
7.基类的protected成员:可以被基类的成员函数,基类的友元函数,派生类的成员函数可以访问当前对象的基类的保护成员。
8.派生类的构造函数:先执行基类构造函数,再执行派生类构造函数。
派生类交代基类初始化具体形式:
构造函数名(形参表):基类名(基类构造函数实参表){}
9.public继承的赋值兼容规则:(1)派生类对象可以赋值给基类对象。(2)派生类对象可以初始化基类引用。例如:Base & b = d,其中b是基类引用。d是派生类对象。
(3)派生类对象的地址可以赋值给基类的指针 Base *pb = &d 注:如果派生不是以public派生,则上述三条不成立。
10.直接基类和间接基类:
有下述派生关系:A->B->C->D
A是B的直接基类,B是C的直接基类,依次类推。
A是C,D的间接基类,依次类推。
*******************************************************************************************
1.虚函数:virtual关键字只能用在类定义里的函数声明中,写函数体时不用。构造函数和静态成员函数不能是虚函数。跟普通成员函数相比,虚函数就是能参与多态。
2.多态的表现形式一:
(1)派生类指针可以赋值给基类指针
(2)通过基类指针调用基类和派生类中的同名虚函数时:若该指针指向一个基类对象,那么被调用是基类的虚函数。若该指针指向一个派生类的对象,那么被调用的是派生类的虚函数。
3.多态的表现形式二:
(1)派生类的对象可以赋值给基类引用
(2)通过基类引用调用基类和派生类中的同名虚函数时:若该引用引用的是一个基类对象,那么被调用是基类的虚函数。若该引用引用的是一个派生类的对象,那么被调用的是派生类的虚函数。
4.多态的作用就是增加程序的可扩充性。
5.多态的关键在于通过基类引用或者基类指针调用一个虚函数时,编译时不确定到底调用基类还是派生类的函数,运行时才能确定---这叫做“动态联编”。
6.多态实现的关键----虚函数表(VFT):每一个有虚函数的类(或有虚函数类的派生类),都有一个虚函数表,该类的任何对象中都存放着虚函数表的指针,虚函数表中列出了该类虚函数的地址。多出来的四个字节就是存放虚函数表的地址的(在32位机器的情况下,64位就是8字节)。
7.虚析构函数的引入:
通过基类的指针删除派生类对象时,会只调用基类的析构函数。但是我们实际上需要先调用派生类的析构函数再调用基类的析构函数。
如果把基类的析构函数声明为virtual,那么派生类的析构函数可以不进行显式声明,直接就是虚析构函数了。类如果定义了虚函数,则最好将析构函数也定义为虚函数。
8.纯虚函数是指没有函数体的虚函数。virtual type fuc() = 0;
9.包含纯虚函数的类就叫抽象类。抽象类只能作为基类来派生新类。不能创建抽象类的对象。抽象类的指针和引用能指向或引用由抽象类派生出来的类的对象。
10.在抽象类中,在成员函数内可以调用纯虚函数,在构造函数和析构函数中不能调用纯虚函数。如果一个类从抽象类中派生而来,那么必须实现基类中所有的纯虚函数才能成为非抽象类。
***********************************************************************************************************
1.文件和流:顺序文件就是一个有限字符构成的顺序字符流。ifstream,ofstream,fstream这三个类统一成为文件流类。用于文件操作。
2.ios类派生出了istream和ostream类,istream派生除了ifstream和iostream。ostream派生出了iostream和ofstream。iostream派生出了fstream。
3.文件分两类,一种是二进制格式的,一种是文本格式的(主要指ASCII)。尽管来说文本格式实现也是二进制编码,但是这里的文本格式是站在更高层次来说明,二进制的格式就是文本方式打开看不到正确的实际内容。
4.文本文件和二进制文件打开的区别:在Unix/Linux下,二者一致,没什么区别。在windows下,文本文件是以“ ”作为换行符的,读出时,系统会将0x0d0a只读入0x0a,写入时,对于0x0a系统会自动写入0x0d。
5.泛型程序设计就是算法实现时不指定具体要操作的数据的类型-----适用于多种数据结构,减少重复代码的编写。
6.模版分为两类:函数模版和类模版
7.函数模版写法:
template<class 类型参数1,class 类型参数2,...>
返回值 模版名(形参表)
{
}
8.函数模板也可以被重载。只要形参表不一样即可。建议为了避免参数二义性,建议用不同的类型参数代替。template<class T1, class T2> func(T1 & a, T2 & b)
9.类模版的定义:
template<class 类型参数1,class 类型参数2,...>
class 类模版名
{
成员函数和成员变量
}
成员函数在类模版外定义:
template<class 类型参数1,class 类型参数2,...>
返回值类型 类模版名<类型参数名列表>::成员函数名(参数表)
{
}
用类模板定义对象:
类模板名 <真实类型参数表> 对象名(构造函数实际参数表)
如果类模版有无参构造函数:
类模版名 <真实类型参数表> 对象名 template_name <string,int> student("Tom", 19)
10.类模版的类型参数表中可以写<class 类型参数1, int elemet_name>,依次递推。
11.类模版也可以派生。
*****************************************************************************************************
1.STL中的基本概念:
容器:可容纳各种数据类型的数据结构,是类模版
迭代器:可用于依次存取容器中的元素,类似于指针
算法:用来操作容器中的元素的函数模版,例如sort用来对一个vector中的数据进行排序,find用来搜索list中的对象
2.容器概述:
顺序容器:vector,deque,list
关联容器:set,multiset,map,multimap
容器适配器:stack,queue,priority_queue
3.对象被插入容器中时,被插入的对象是个复制品。许多需要通过比较的算法,所以放入容器的对象所属的类,往往还应该重载==和<运算符
4.顺序容器并非是排序的,元素的插入位置和元素的值无关。
5.vector,头文件vector,动态数组。元素在内存中连续存放。随机存取任何元素都能在常数时间内完成。在尾端增删元素时有较佳性能,大部分时间是常数时间。(一般会多分配些内存空间)
deque,头文件deque,双向队列,元素在内存中连续存放。随机存取任何元素都能在常数时间内完成(但次于vector)。在两端增删元素具有较佳的性能(大部分时间是常数时间)
list,头文件list,双向链表,元素在内存中不连续存放。在任何位置增删元素都能在常数时间完成。不支持随机存取。
6.关联容器元素是排序的,插入任何元素,都按相应的排序规则来确定其位置,在查找时具有非常好的性能。通常以平衡二叉树实现,插入和检索时间都是O(log(N))
set/multiset 头文件set set即集合,所以不允许存在相同的元素,sultiset中允许存在相同的元素。
map/multimap 头文件map map与set的不同之处在于map中存放的元素有且仅有两个成员变量,一个名为first,另一个名为second,map根据first值对元素进行从小到大排序,并可快速的根据first来检索元素。
map/multimap的不同在于是否允许相同first值的元素。
7.容器适配器,stack自然不必多说。queue,队列,插入只可以在尾部进行,删除,检索和修改只允许从头部进行。priority_queue,优先级队列,保证优先级最高的元素在头部,删改只能在头部。
8.顺序容器和关联容器都有的成员函数:
begin 返回指向容器中第一个元素的迭代器
end 返回指向容器中最后一个元素后面的位置的迭代器
rbegin 返回指向容器中最后一个元素的迭代器
rend 返回指向容器中第一个元素前面的位置的迭代器
erase 从容器中删除一个或几个元素
clear 从容器中删除所有元素
9.顺序容器的常用的成员函数:
front 返回容器中第一元素的引用
back 返回容器中最后一个元素的引用
push_back 在容器末尾添加新元素
pop_back 删除容器末尾的元素
erase 删除迭代器指向的元素(可能会使该迭代器失效),或删除一个区间,返回被删除元素后面那个元素的迭代器
10.迭代器-------用于指向顺序容器和关联容器的元素,迭代器用法和指针类似,有const和非const两种,通过迭代器可以读取它指向的元素,通过非const迭代器还能修改其指向的元素。
11.定义一个容器类迭代器的方法:
容器类名::iterator 变量名
容器类名::const_iterator 变量名
容器类名::reverse_iterator 变量名
访问一个迭代器指向的元素: *迭代器变量名
迭代器也可以使用++,指向下一个元素,如果迭代器指到最后一个元素的后面,再使用它就会出错。
12. 容器 容器上的迭代器类型
vector 随机访问
deque 随机访问
list 双向
set/multiset 双向
map/multimap 双向
stack 不支持迭代器
queue 不支持迭代器
priority_queue 不支持迭代器
13.算法,头文件algorithm,可以处理容器,也可以处理数组。
14.迭代器的区间一般都是左闭右开的。[start ,end)
15.vector可根据下标随机访问,所以常数时间。
16.vector中的成员函数:
vector() 无参构造函数,将容器初始化为空
vector(int n) 将容器初始化为有n个元素
vector(int n, const T & val) 将容器初始化为有n个元素,并且每个元素的值为val
vector(iterator first, iterator last) 将容器初始化为与别的容器上区间[first,last)一致的内容。
17.list容器其他的成员函数;
remove 删除和指定值相等的所有元素
unique 删除所有和前一个元素相同的元素,其实就是让一个元素的值在list中唯一存在
merge 合并两个链表,并清空被合并的链表
splice 在指定位置前面插入另一个链表中的一个或者多个元素,并在另一个链表中删除被插入的元素。
18.不能用标准库中的sort对list进行排序,list容器只能只用双向迭代器,不支持比较运算符和[]运算符和随机移动(即list迭代器+2之类的操作)
19.所有适用vector的操作都使用于deque
20.如果一个类重载了()运算符,则该类的对象就称为了函数对象
*******************************************************************************************************