(1)Class分类:
class without pointer members ——>e.g: complex 复数
class with pointer members ——>e.g: string 字符串
(1)标准库以头文件的形式存在,只需要include进去就好#include <*.h>
(2)CPP防卫式头文件
#ifndef _HEAD_
#define _HEAD_
//...
#endif
在调用这个头文件的时候,会判断是否被定义过,定义过了就不会再执行了,要注意两个不同的头文件不能重名。适合的场景:cpp文件调用了许多头文件,头文件可能会互相包含,加入这个的话调用头文件次序就无所谓了,这样头文件只会被定义一次。
(1)内联函数(inline):在函数前面加 inline 关键字,对编译器的建议精良inline。
pair(const T1& a, const T2& b) : first(a), second(b) {}
初始化列表和在body对参数赋值的区别:一个参数初始化;一个赋值,是一个执行的过程,增加计算量。
(3)函数重载(overloading):函数名称一样,但是函数的参数个数或参数类型不一样或有无 const,与返回值类型无关。
(4)友元函数(friend):类的友元函数能直接拿到该类对象的私有数据,相同class的各个objects互为friends
(1)const 定义常量、不可变。 对不会改变的类函数,加const。cg. double real() const {return re;}
(2) pass by value, pass by reference
(3)不可以 pass by reference的情况:当变量是类的内部函数是,函数调用结束,值对应的空间就释放了,如果传引用,该引用就是一个坏值。
(1)操作符重载,分为两种形式,成员函数和非成员函数
(2)临时对象,在下一行被杀死:typename()
(1)Big three
(2)对于class中含有指针的,必须要定义big three。
(3)自定义拷贝构造函数和拷贝赋值函数,来实现深拷贝,而不是默认的浅拷贝。 浅拷贝:造成内存泄漏,造成有两个指针指向同一块内存。
(1)new: 先分配memory,在调用构造函数
(2)delete:先调用析构函数,再释放内存。
(3)array new一定要搭配array delete,不然容易造成内存泄漏。
(1)static:静态。静态数据不属于某一个对象。静态函数没有this pointer,静态函数只能处理静态数据。静态数据一定要在class外定义。调用static函数方法有二:通过object调用;通过class name调用。
(2)template:类模板,函数模板
(1)组合 composition:表示 has-a 的关系。组合关系下的构造与析构函数:
(2)委托 Delegation,即composition by reference:在body中声明一个 带指针的 另一个类 ,classA 用一个指针指向classB,需要的时候才调用classB,而不是一直拥有classB。
(3)继承 Inheritance:表示 is-a 的关系。
(1)纯虚函数 virtual,纯虚函数一定要重新定义。
(2)Inheritance + composition下的构造和析构 顺序。
(3)delegation + Inheritance,功能最强大的一种。
(1)把对象转换成另一种类型。
例如把一个分数类的对象转换为 double值,定义一个成员函数 operator double() const { return double 分子/分母} 。 不可以有参数,并且没有返回值类型。 在后面如果需要用到double的对象,会自动的转换,不需要调用函数。
(2)把另一个类型的对象转化为这一种类型。
non-explicit-one-argument ctor:最少需要一个参数的构造函数。下面程序,会把4转化为fraction。
#include
#include
using namespace std;
class fraction
{
public:
fraction(int num, int den=1):m_n(num),m_d(den) {}
int operator + (const fraction &f) {
return 2;
}
private:
int m_n;
int m_d;
};
int main() {
fraction f(5, 2);
int i = f + 4;
cout << i;
}
(3)explicit-one-argument ctor:在构造函数之前加explicit,表示显示的,只有调用构造函数才可以构造,不像(2)里面会自动构造。
(1)关于智能指针和关于迭代器。讲了一下指针的操作符重载。
(1) 对操作符 () 进行重载的class 就是 function-like class。
(1)函数模板,类模板:函数模板直接给实参;类模板需要在尖括号指明类型。
//类模板
template
class complex{ }
//函数模板
template
inline const T& min(const T&a, const T*b){}
class和typename一样。
(2)成员模板:模板里面再定义模板。
(3)模板特化:使用模板时会遇到一些特殊的类型需要特殊处理,此时就需要对该类型特化出一个模板函数(就是写出一个模板函数专门给该类型使用)
//泛化
template struct __type_traite{}
//特化
template<> struct __type_traite{}
//特化
template<> struct __type_traite{}
(4)模板偏特化:对函数模板的一部分模板类型进行特化。
//偏特化类型一:数量上的局部, 一部分参数锁定。
//泛化
template class vector{}
//偏特化
template class vector
//偏特化类型二:范围的局部。
//泛化
template struct iterator_traits{}
//偏特化,指针指向任意都可以
template struct iterator_traits{}
//偏特化
template struct iterator_traits{}
(5)模板模板参数:模板的参数类型也是模板。
(1)variadic templates数量不定的模板参数: 注意…的位置,…表示一系列表示一个所谓的pack(包)。 最上面的那个空的print()必须要写,因为到最后一个参数时,print函数参数为空。
void print()
{
}
template
void print(const T &first, const Types &...args) {
cout << first << endl;
print(args...);
}
int main() {
print(7.5, "hello", bitset<16>(377), 42);
return 0;
}
(2)auto:编译器自动生成类型。
(3)ranged-base for:for的新形式。for( dec1: col1) { statement } 。col即容器
eg. for(int i :{1,2,3,4,5}) {} 。i会依次取值。
(1)引用即别名,定义以后 不能再指向其他的变量。object和reference大小相同、地址也相同。
cpp动态多态就是靠虚函数实现。在申明虚函数的时候,编译器自动生成虚函数表,虚表中存储的是 虚函数的入口地址。当存在虚函数时,每个对象都有一个指向虚函数表的指针,即虚指针vptr。虚指针指向虚标的入口。
在基类中申明虚函数,然后子类覆盖了虚函数,那么子类的虚表中就存的是新的虚函数的地址。一个类继承了包含虚函数的基类,那么这个类会生成自己的虚表。同一个类的所有对象都使用同一个虚表。
假设有一个基类的指针p指向 子类的对象, 虽然p是基类的指针只能指向基类的部分,但是vptr属于基类的部分,所以p可以访问子类对象的虚指针,指向子类自己的虚函数。
实现动态绑定的三个条件:1.通过指针来调用函数; 2.调用虚函数; 3.父类指针指向子类对象。
class A {
public:
virtual void vfun1() { cout << "A:vfun1" << endl; }
virtual void vfun2() { cout << "A:vfun2" << endl; }
void fun1() { cout << "A:fun1" << endl; }
void fun2() { cout << "A:fun2" << endl; }
};
class B :public A {
public:
virtual void vfun1() { cout << "B:vfun1" << endl; }
void fun2() { cout << "B:fun2" << endl; }
};
int main() {
A a;
B b;
A *p = &b;
p->vfun1();
p->vfun2();
p->fun1();
p->fun2();
B *pp = &b;
pp->vfun1();
pp->vfun2();
pp->fun1();
pp->fun2();
}
##输出结果
B:vfun1
A:vfun2
A:fun1
A:fun2
B:vfun1
A:vfun2
A:fun1
B:fun2
指向调用这个成员函数或者正在被构造的对象。存在于类的非静态成员中。
(1)const 修饰成员函数,意为该成员函数不会改变class的data,const位置放在大括号前。 eg. double real() const {return re;}。const的对象不可以调用 non-const的成员函数,会报错。当成员函数的const和non-const的版本同时存在,const object只会调用const版本,non-const object只会(只能)调用non-const函数。
(2)const修饰对象需要初始化。
(3)const与指针: non-const指针不可以指向const对象,其他都可以。const修饰指针有三种方式:修饰指针指向的内容;修饰指针;修饰指针和指针指向的内容。
int a=8;
const int *p=&a;
表示指针指向的内容是常量。指针可以更改指向的内容,但是不能通过指针去更改指向内容的值,但是 那个值可以通过自己改变。
int a=8;
int *const p=&a;
表示指针指向的地址不可变,指针是个常量。但内容可以变。即 a的值可以变化,但是p只能指向a。
int a = 8;
const int * const p = &a;
表示指针指向的内容和指向的内存地址都不可改变。
(4)const参数传递和函数返回值: