cpp笔记

P1:c++编程简介

(1)Class分类:
class without pointer members ——>e.g: complex 复数
class with pointer members ——>e.g: string 字符串

P2:头文件与类的声明

(1)标准库以头文件的形式存在,只需要include进去就好#include <*.h>
(2)CPP防卫式头文件

#ifndef _HEAD_
#define _HEAD_
//...
#endif

在调用这个头文件的时候,会判断是否被定义过,定义过了就不会再执行了,要注意两个不同的头文件不能重名。适合的场景:cpp文件调用了许多头文件,头文件可能会互相包含,加入这个的话调用头文件次序就无所谓了,这样头文件只会被定义一次。

P3:构造函数

(1)内联函数(inline):在函数前面加 inline 关键字,对编译器的建议精良inline。

  • 函数太复杂,无法inline
  • 函数在body中定义,自动inline,不需要申明
  • 在body外定义,要声明。在class中声明函数时可以不写inline,在外部实现在加inline。
    内联函数为什么快:普通函数调用,需要靠栈维护,函数调用前后的环境、参数传递等。内联函数是在调用出进行代码展开,而不是真正的函数调用,所以快。
    (2)构造函数,用冒号设置初始化列表。
pair(const T1& a, const T2& b) : first(a), second(b) {}

初始化列表和在body对参数赋值的区别:一个参数初始化;一个赋值,是一个执行的过程,增加计算量。
(3)函数重载(overloading):函数名称一样,但是函数的参数个数或参数类型不一样或有无 const,与返回值类型无关。
(4)友元函数(friend):类的友元函数能直接拿到该类对象的私有数据,相同class的各个objects互为friends

P4:参数传递与返回值

(1)const 定义常量、不可变。 对不会改变的类函数,加const。cg. double real() const {return re;}
(2) pass by value, pass by reference

  • pass by value:传递数值,慢,不建议
  • pass by reference:传递引用,相当于指针,快
  • pass by reference to const:传递的指针不可改变
    返回值传递也尽量pass by reference

(3)不可以 pass by reference的情况:当变量是类的内部函数是,函数调用结束,值对应的空间就释放了,如果传引用,该引用就是一个坏值。

P5:操作符重载

(1)操作符重载,分为两种形式,成员函数和非成员函数

  • 成员函数:带有隐藏参数‘this’,谁调用函数谁就是this
  • 非成员函数: ’ << ’ 只能重载为非成员函数。

(2)临时对象,在下一行被杀死:typename()

P7:big three:拷贝构造、拷贝赋值、析构函数

(1)Big three

  • 拷贝构造函数(CA(const CA & other))
  • 拷贝赋值函数(CA &operation = (const CA & other))
  • 析构函数

(2)对于class中含有指针的,必须要定义big three。
(3)自定义拷贝构造函数和拷贝赋值函数,来实现深拷贝,而不是默认的浅拷贝。 浅拷贝:造成内存泄漏,造成有两个指针指向同一块内存。

P8:堆,栈与内存管理

(1)new: 先分配memory,在调用构造函数
(2)delete:先调用析构函数,再释放内存。
(3)array new一定要搭配array delete,不然容易造成内存泄漏。

P10:扩展补充:类模板,函数模板,及其他

(1)static:静态。静态数据不属于某一个对象。静态函数没有this pointer,静态函数只能处理静态数据。静态数据一定要在class外定义。调用static函数方法有二:通过object调用;通过class name调用。
(2)template:类模板,函数模板

P11:组合与继承

(1)组合 composition:表示 has-a 的关系。组合关系下的构造与析构函数:

  • 构造:由内而外,Container的构造函数首先调用Component的default构造函数,然后才执行自己。
  • 析构:由外而内,Container的析构函数首先执行自己,然后才调用Component的析构函数。

(2)委托 Delegation,即composition by reference:在body中声明一个 带指针的 另一个类 ,classA 用一个指针指向classB,需要的时候才调用classB,而不是一直拥有classB。
(3)继承 Inheritance:表示 is-a 的关系。

  • 构造:由内而外进行,Derived的构造函数首先调用Base的default构造函数,然后才执行自己。
  • 析构: 由外而内进行,Derived的析构函数首先执行自己,然后才调用Base的析构函数。

P12:虚函数与多态

(1)纯虚函数 virtual,纯虚函数一定要重新定义。
(2)Inheritance + composition下的构造和析构 顺序。
(3)delegation + Inheritance,功能最强大的一种。

P15:转换函数 conversion function

(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)里面会自动构造。

P17: pointer-like class

(1)关于智能指针和关于迭代器。讲了一下指针的操作符重载。

  • 关于智能指针:一定要重载*和->
  • 关于迭代器,有更多的需要重载,一定要重载++,–(对应于迭代器指针的移动, 智能指针不需要重载这些。)
  • 操作符 -> 会沿用下去。

P18: function-like class

(1) 对操作符 () 进行重载的class 就是 function-like class。

P20~25:模板

(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)模板模板参数:模板的参数类型也是模板。

P27:三个主题(for C++11)

(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会依次取值。

P30:reference

(1)引用即别名,定义以后 不能再指向其他的变量。object和reference大小相同、地址也相同。

P31: vptr和vtbl

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

P31:this指针

指向调用这个成员函数或者正在被构造的对象。存在于类的非静态成员中。

P33:const

(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修饰指针有三种方式:修饰指针指向的内容;修饰指针;修饰指针和指针指向的内容。

  • 修饰指针指向的内容:const放在 * 左边。
int a=8;
const int *p=&a;

表示指针指向的内容是常量。指针可以更改指向的内容,但是不能通过指针去更改指向内容的值,但是 那个值可以通过自己改变。

  • 修饰指针:const放在*右边。
int a=8;
int *const p=&a;

表示指针指向的地址不可变,指针是个常量。但内容可以变。即 a的值可以变化,但是p只能指向a。

  • 修饰指针和指针指向的内容:const放在两边
int a = 8;
const int * const  p = &a;

表示指针指向的内容和指向的内存地址都不可改变。
(4)const参数传递和函数返回值:

你可能感兴趣的:(后端)