C++应该算是我的第二语言,做过一个相关项目,学习书籍是《C++ primer plus》蓝色的那一版本
我把我认为容易忘记的一些点就记录在这里了。都是基础,适合新手
C++
- x++先执行x再执行++,++x先执行++再执行x。++x比x++运行速度快,因为x++需要先创建一个x的副本。
- #include “ ”在当前目录底下寻找头文件,若没有再去标准位置查找;#include<>在标准头文件中寻找。
- cin.get()读取到’\0’,保留回车;cin.get(str,arrSize);cin.getline()读取到’\0’,删除缓冲区的回车;cin.getline(str.arrSize);
- const type name=value;可以定义一个常量。
- enum spectrum{red , orange , yellow , green , blue=7 , violet , indigo , ultraviolet}。通常他们对应的是int型(转换后)0-7,如果有显式则后面的+1。
- int* p=new int;delete p;p是一个指向int类型空间的指针,位置系统分配,使用完毕用delete删除。int* p=new int[x];delete[] p;p是一个指向长度为x(可以为变量)的动态数组的指针,使用同普通数组一样使用;ar若为数组名,ar[i]可以被解释为*(ar+i)。
- 文件流,例如输出文件ofstream outFile; outFile.open(“fish.txt”); outFile<<str;outFile.close(); 输入文件ifstream inFile; inFile.open(“fish.txt”); ifFile>>value; inFile.close();可以把它们看作是cin和cout在使用。
- 在函数中需要数组作为参数的时候例如函数 int sumArr(int arr[], int n); 此处也可以写为int sumArr(int* arr, int n); 在C++中,arr[i]==*(arr+i), &arr[i]==arr+i; 数组传递是址传递,单变量是值传递。
- 常量保护,void showArr(const double arr[],int n),这样原始数组就不会被无意之间在showArr这个函数中被修改了。
- 函数指针,获取函数地址,只是用函数名不加()即可。声明和应用,int pam(int); int (*p)(int)。即在函数原型把名称替换即可。利用函数指针则可以方便的在函数中调用函数。
- typedef double real;
- 引用int& x=y;x成为y的一个别名;一般用在引用传递(效果同址传递);若避免在函数中修改了参数信息可以用int cube(const int &x)(这即使用了原来的变量也避免修改了它,提升运行速度)
- 内联函数inline 可以提升运行速度,程序不会跳到代码块而是直接替换代码,在函数代码少的时候可用。
- template <typename T>模板编程,减少代码重复量,但是在实际中由编译器实例化函数。
- 在头文件中,通常包括:函数原型,#define或者const定义的符号常量,结构声明,类声明,模板声明,内联函数
- 用#ifndef .h #define .h #endif来避免同一个头文件被多次编译。
- 布局new操作符,可以查找某些特定的内存空间
- 通常将函数原型放在一个文件,函数定义放在一个文件,main以及其他的放在一个文件。
- 只要类方法不修改调用对象,我们就将其声明为const,void show()const。
- 要创建对象数组,则这个类必须包含默认构造函数,因为系统会先利用默认构造函数创建对象,再将{}中的数据作为一个副本复制到对象数组中。
- 利用operator+()来重载“+”,操作符重载满足一些原始规则
- friend关键字只能用在函数原型中,使得非成员函数可以访问成员变量,通常出现在操作符重载中。
- 例如:Time operator+(const Time &t)const; friend Time operator+(const Time &t1,const Time &t2); 前者为成员函数,其中一个参数用this指针隐示传递,后者非成员函数,访问成员变量需要用friend修饰,显式传参。
- 如果在构造函数中使用了new,那么在析构函数中则需要使用delete来释放内存。
- 当在构造函数中出现了用new分配内存,或原始对象指向一个数据块,创建副本的时候是浅拷贝,类的析构函数会删除同一内存两次,所以需要定义复制构造函数T(const T& t)并重载赋值操作符(“=”)。
- 如果使用布局操作符new为类对象分配内存,则需要显示的为其调用析构函数,方法为使用指向对象的指针。
- 类在内存中大小,空类为1字节(系统隐含添加一个字节,作为区分),存在虚函数不论多少为4字节(包含一个指向虚函数表的指针vptr),普通数据成员为其类型所占大小(注意字节对齐,可以理解为都为最大所需内存类型,有int有char则为8),静态变量在类中不占空间(单独存在于外面),类大小与成员函数无关,普通继承类大小为基类数据大小加本身数据大小,虚拟继承则再加上一个4字节的指针列表。
- 构造函数用初始化成员列表的方式为非静态const和引用初始化,其完成在进入构造函数之前。T(int x):const_varible(x);
- 派生类必须提供自己的构造函数,在创建对象的时候,程序先调用基类的构造函数,即在进入派生类构造函数之前生成基类对象。程序需要初始化成员列表的形式将基类信息传递给基类构造函数。
- 公有继承建立is-a关系(is a kind of),不建议建立has-a关系,is-like-a关系,is-implemented-as-a关系,uses-a关系。
- virtual将函数声明为虚函数。如果不加virtual,程序会根据引用类型或者指针类型来确定调用基类或者派生类方法。加了virtual则根据引用或者指针所指向对象来决定调用哪种方法。经常在基类中将派生类会重新定义的方法声明为虚方法。同时为基类定义一个虚拟析构函数也是一种惯例。
- 静态联编,编译器通过查看函数名,函数参数决定使用哪个函数。动态联编,在程序运行时选择哪种对象的函数。系统对虚函数进行动态联编。
- 定义为virtual的函数,在基类中和派生类中应该保持完全一致,它不会重载,而是隐藏其他的(基类或者派生类中)相同虚函数。但是如果返回的类型是基类引用或者指针,则在派生类中可以修改为指向派生类的指针或者引用
- Protected类型对外界同private相同,对派生类同public相同。尽量不要使用protected类型,数据用private类型保护,通过public的方法访问。
- 在基类中可以定义纯虚函数virtual void fun()=0,包含纯虚函数的类不能实例化,派生类中必须实现纯虚函数。基类中可以对纯虚函数加定义,在派生类中可以通过作用域解析符(“::”)来调用基类的纯虚函数。virtual void fun()=0{}。(同java abstract)
- 友元不能是虚函数,因为友元不是类成员。可以通过让友元函数使用虚成员函数来解决。
- 如果在派生类中重载基类中的虚函数(实际上没有重载),无论参数列表是否相同,该操作都会隐藏所有同名的基类虚方法。(隐藏则无法使用)
- valarray类,valarray<int> v1。构造函数valarray<int> v1,valarray<int> v1(8)创建8个元素,valarray<int> v1(10,8)创建8个元素初始化为10,valarray<int> v1(v2,4)创建4个元素初始化为v2数组前4个元素。类方法operator[]()访问各个元素,size()返回包含元素数,sum()返回元素和,max(),min()等。
- c++在处理只有一个参数的构造函数的时候会有一个隐式的转换例如类T(int x){y=x},实例化时可以使用T t=10;此处隐式的将其等效为T t(10);如果使用explicit修饰构造函数则将取消这种隐式转换。
- 用成员列表初始化的时候,初始化顺序是按照成员被声明的前后顺序而不是成员列表中的顺序。
- 私有继承,类将继承实现。例如Class T{private:string name},利用私有继承实现Class T:private string{}。区别1前者提供了显式命名的对象成员,后者提供了无名称的对象成员。区别2初始化成员列表的时候前者是T(const char* str):name(str){},后者是T(const char* str):std::string(str){}。使用包含时将使用对象名来调用方法,使用私有继承时将使用类名和作用域解析符来调用方法。例如name.strlenth(),string::strlenth()。访问基类对象的时候利用*this和强制类型转换。访问基类友元函数的时候需要进行显式的类型转换,例如ostream& operator<<(ostream& os,const T &t){os<<(string&) stu;}
- 通常应该用包含来建立has-a关系,但如果新类需要访问原有类的保护成员或需要重新定义虚函数,则使用私有继承
- 多重继承,应当用public限定每一个基类,否则将为标明的默认设置为私有派生。假设有A,BC继承A,D多重继承BC,则会出现D中出现两次A类的问题。此时我们需要用虚基类。class A; class B:virtual public A; class C:virtual public A; class D:public B,public C;C的构造函数为了避免二义性则需要显式的调用A的构造函数(否则用默认的)。在方法调用上通常重写D中的方法,不过也可以利用作用域解析符“::”来调用BC中的方法。
- 类模板,同样使用template关键字。例如template<typename T> class Stack;
其中数据类型可以用T代替,在实现的时候将类限定符“Stack::”改为“Stack<T>::”。
- 友元类,可以在一个类中将另外一个类声明为友元,那么在友元类中则可以访问它的成员。例如一个电视机类TV可以将遥控器类Remote设置为友元类。
- 异常机制,通过throw-try-catch()来将获取处理异常的控制权。
- string类,构造函数有6个string(const char* s),string(size_type n,char c)包含n个元素并初始化为字符c,string(const string& str,string size_type n=npos()复制字符串从pos开始到结尾或者第n个字符,string()创建默认对象长为0,string(const char* s,size_type n)初始化为s指向NBTS前n个字符,template<class Iter> string(Iter begin,Iter end)将对象初始化为[begin,end)内的字符。
- 字符串输入,C-风格字符串有三种方式:char info[100]; cin>>info; cin.getline(info,100); cin.get(info,100); 对于string对象有两种方式:string stuff; cin>>stuff; getline(cin,stuff);
- 使用string字符串,例如find(),+,=,<,>,==等等。
- 智能指针auto_ptr,定义了类似指针的对象,可以将new(不可以new[])获得的地址赋给该对象,当auto_ptr过期的时候自动调用析构函数删除它所指向的内存。例如string* ps=new string(str);可以变为#include<memory> auto_ptr<string> ps(new string(str));
- vector<int>vel定义一个vector容器,利用迭代器vector<int>::iterator pd可以对其进行操作,或者是像数组一样操作。迭代器是STL的接口,和指针基本类似,通常包括输入迭代器,输出迭代器,双向迭代器,正向迭代器,随机访问迭代器。
- 容器类型,vector通常用这个除非其他容器的特殊性质比这个好,deque双端队列,list链表,queue队列,priority_queue同queue但最大的元素移到队首,stack堆栈,以及集合容器,set可以自动排序,multimap同set但是可以存多种类型。