多态:
1.强制多态
2.重载多态
3.类型参数化多态(模板)
4.包含多态(虚函数)
内存存储:
-
堆(stack):由程序员分配,动态内存分配,程序运行时分配,可以随时创建和释放,手动释放
-
栈(heap):由编译器自动分配,静态内存分配,程序编译时分配,分配完不能变,自动释放
-
运行栈:用来存储函数的形参和局部变量(后进先出),实际上是一段区域的内存空间,与存储全局变量的空间无异,只是寻址方式不同。运行栈中的数据分为一个一个栈帧
-
只读数据段:离栈很远,用于存字符串(文字常量区)
-
程序代码区域
-
全局区(静态区)static:在内存中以固定地址存放的,在整个程序运行其间都有效
指针、数组、函数:
指针数组:int *p[3] 数组中每个元素都是指针
数组指针:int (*p)[3] 指向数组的指针
数组不能整体赋值
指针变量加1,其结果相当于加1个其指向的字节数
int *p = (int*)5
p++
p == 9
p[3] <==> *(p+3)
a[3] <==> 3[a]
指针是地址变量
数组是地址常量
char *p = NULL; 野指针,指向不确定
void *p 泛型指针,可以赋值给任意类型指针变量
函数名是函数所占内存区的起始地址
int(*p)(); 指向函数的指针
将函数的参数声明为数组和指针是一样的
union:
联合体的全部数据成员共享同一组内存单元
#include<iostream>
#include<string>
using namespace std;
class ExamInfo
{
public:
ExamInfo()
:name(name)
private:
string name;
enum {
GRADE,
PASS,
PERCENTAGE
} mode;
union{
char grade;
bool pass;
int percent;
};
};
void ExamInfo::show()
{
count<<name<<":";
switch(mode)
{
case GRADE:
cout << grade;
break;
case PASS:
cout << (pass ? "PASS" : "FAIL");
break;
case PERCENT:
cout << percent;
break;
}
}
枚举
#include <iostream>
using namespace std;
enum GameResult {WIN,
LOST,
TIE,
CANCEL};
int main()
{
GameResult result; //声明变量时,可以不写关键字enum
enum GameResult omit = CANCEL; //也可以在类型名前写enum
for(int count = WIN; count <= CANCEL; count++) //隐式类型转换
{
result = GameResult(count); //显式类型转换
if(result == omit)
{
count<<"game cancel"<<endl;
}
else
{
if(result == WIN)
{
cout<<"WIN"<<endl;
}
if(result == LOST)
{
cout<<"LOST"<<endl;
}
count<<endl;
}
}
return 0;
}
enum、define、const
enum和define不占内存,由编译器进行值的替换,无类型检查
const占内存,有类型检查
类的组合:
#include<iostream>
#include<cmatch>
using namespace std;
class Point
{
public:
Point(int xx = 0, int yy = 0)
{
x = xx;
y = yy;
}
Point(Point& p);
int getX()
{
return x;
}
int getY()
{
return y;
}
private:
int x, y;
};
Point::Point(Point &p)
{
x = p.x;
y = p.y;
cout<<"Calling the copy constructor of point"<<endl;
}
//类的组合
class Line
{
public:
Line(Point xp1,Point xp2);
Line(Line &l);
double getLen()
{
return len;
}
private:
Point p1, p2;
double len;
}
//组合类的构造函数
Line::Line(Point xp1, Point xp2): p1(xp1), p2(xp2)
{
cout<<"calling constructor of Line"<<endl;
double x = static_cast<double>(p1.getX() - p2.getX());
double y = static_cast<double>(p1.getY() - p2.getY());
len = sqrt(x * x + y * y);
}
//组合类的复制构造函数
Line::Line(Line &l): p1(l.p1), p2(l.p2)
{
cout<<"calling the copy contructor of line"<<endl;
len = l.len;
}
调用内嵌对象的构造函数,调用顺序按照内嵌对象在组合类的定义中出现的次序。注意,内嵌对象在构造函数的初始化列表中出现的顺序与内嵌对象构造函数的调用顺序无关。
构造函数和析构函数:
-
派生类的构造函数只负责对派生类新增的成员进行初始化,对所有从基类继承下来的成员,其初始化工作是由基类的构造函数完成
-
派生类构造函数执行的一般次序如下:
- 调用基类构造函数,调用顺序按照它们被继承时声明顺序(从左向右)。
- 对派生类新增的成员对象初始化,调用顺序按照它们在类中声明的顺序。
- 执行派生类的构造函数体中的内容。
#include<iostream>
using namespace std;
class Base1
{
public:
Base1(int i)
{
cout<<"base1"<< i <<endl;
}
}
class Base2
{
public:
Base1(int j)
{
cout<<"base2"<< j <<endl;
}
}
class Base3
{
public:
Base3()
{
cout<<"base3 *"<<endl;
}
}
class Derived : public Base2, public Base1, public Base3
{
public:
Derived(int a, int b, int c, int d) : Base1(a), member2(d), member1(c), Base2(b)
{}
private:
Base1 member1;
Base2 member2;
Base3 member3;
}
int main()
{
Derived obj(1, 2, 3, 4);
return 0;
}
结果:
Base2 2
Base1 1
Base3 *
Base1 3
Base2 4
Base3 *
static:
定义时未指定初值的基本类型静态生存期变量,会被赋予0值初始化,而对于动态生存期变量,不指定初值意味着初值不确定。
不能被重载的运算符:
- 类属关系运算符’“.”
- 成员指针运算符".*"
- 作用域分辨符"::"
- 三目运算符"? :"
- 长度运算符"sizeof"
虚函数:
-
虚函数一般不能声明为内联函数,因为对虚函数的调用需要动态绑定,而对内联函数的处理是静态的,所以虚函数一般不能以内联函数处理。但将虚函数声明为内联函数也不会引起错误。
-
虚函数是动态绑定的基础。虚函数必须是非静态的成员函数。
-
如果一个类的析构函数是虚函数,那么由它派生而来的所有子类的析构函数也是虚函数。
-
声明为虚析构函数是为了保证使用基类类型的指针就能够调用适当的析构函数针对不同的对象进行清理工作。
#include<iostream>
using namespace std;
class Base
{
public:
~Base();
};
Base::~Base()
{
cout<<"Base"<<endl;
}
class Derived : public Base
{
public:
Derived();
~Derived();
private:
int * p;
};
Derived::Derived()
{
p = new int(0);
}
Derived::~Derived()
{
cout<<"Derived"<<endl;
delete p;
}
void fun(Base *b)
{
delete b;
}
int main()
{
Base *b = newDerived();
fun(b); //子类析构函数没被调用,内存泄露
return 0;
}
- 基类中仍然允许对纯虚函数给出实现,但即使给出实现,也必须由派生类覆盖,否则无法实例化。
- 如果将析构函数声明为纯虚函数,必须给出它的实现,因为派生类的析构函数体执行完后需要调用基类的纯虚函数。
不使用中间变量交换参数值
a ^= b; //异或
b ^= a;
a ^= b;
a = a + b;
b = a - b;
a = a - b
#include<head.h>和#include“head.h”
#include<>:标准头文件,先查找环境变量中定义的位置
#include"":用户自定义头文件,先查找当前文件目录,然后查找环境变量中定义的位置
Lambda表达式
- Lambda表达式是C++11时引入的特性
- Lambda表达式又被称为匿名函数
- 在句中声明定义的没有名字的无名函数,只有在调用的时候才会创建函数对象
- Lambda本质是一个特殊的,匿名的类类型。它是一个带有operator()的类,即仿函数
- 仿函数(functor),就是使一个类的使用看上去像一个函数。其实现就是类中实现一个operator(),这个类就有了类似函数的行为,就是一个仿函数类了
#include<iostream>
using namespace std;
class AddNum
{
public:
AddNum(int num):_num(num){};
//成员函数
int addNum(int x)const
{
return _num + x;
}
//仿函数
int operator()(int x)const
{
return _num + x;
}
private:
int _num;
};
int main()
{
auto add_num = AddNum(10);
//成员函数调用
auto x = add_num.addNum(5);
//仿函数调用
auto x = add_num(5);
//------------------------------------------------------------------//
//lambda
auto add_num2 = [add_num = 10](int x){return add_num + x;};
auto lambda_x = add_num2(5);
}
访问控制
在c++中,在类的成员函数的参数为此类类型时,可以通过类类型的对象直接访问私有成员变量。或者在成员函数中使用临时对象来直接访问私有变量。
vector
数组和数组中第0个元素,地址相同;而向量和向量第0个元素,地址不同。
swap()只交换向量名字,不交换内存地址