0. 前言:类的分类方法(2种)
对于基于对象编程,类之间还没有建立关系,类看成是单一的class,所以是基于对象的编程
对于成员属性中是否含有指针
complex(class without pointer)
不带指针,创建的对象里放的是具体的数据,不带指针的类多数不用写析构函数
string(class with pointer)
带指针,创建的对象里是放指针的
在创建对象时,编译器会给一套默认的拷贝构造、拷贝赋值以及析构函数,编译器给的函数操作只是简单的一个字节一个字节复制,所以当数据为指针时,就需要自己重写这些函数,不然复制过来的只是地址,那么他们就会指向同一块内存。
p2. 头文件与类声明
像c语言这种面向过程的语言,参数大多是全局的,由于提供的关键字不多,所以参数可供函数之间随便使用。
面向对象语言就是把参数和函数包在一起,你的数据只有你这个函数可以使用,其他函数看不见,并提供了许多关键字,如private,一般定义的属性都是私有的权限。
C++中也有结构体struct,可以理解为class,其与c中结构体主要区别就是默认访问权限的问题,c语言默认是public,c++默认权限是private。
2.1 头文件
2.2 inline内联函数
在类内声明,类内实现的方法称为内联函数,好处是调用速度比较快,
但最终决定这个函数是不是inline由编译器决定,如果函数相对来说比较复杂就不认为是inline函数
p3 普通构造函数
特点: 1.当你创建一个对象时,构造函数会自动调用
2.函数名与类名必须相同
3.可以有参数,且参数可以有默认值
4.没有返回值
5.初始化列表(initialization list)
6.注意构造函数的重载
tip:在单例模式(Singleton)下特殊点是把构造函数放在private里面
p4. 参数传递与返回值
总结:
- 数据一定放在private里
- 参数尽量以reference形式来传,要不要加const看状况
- 返回值首先考虑用reference来传,如果不行采用其他方式,主要就是看函数执行的结果放到什么地方去,如果是local object,就不能用reference来返回;如果函数执行结果的目的端本来就存在,那么就&来返回
- 在类中的函数参数需要加const就必须加
p5. 操作符重载
操作符其实可以理解成一个函数,在数学中或者其他语言中如(C语言),加减乘除的符号就只是简单运算,但为了方便使用,c++允许编程的人自己去设计一种运算方式,你甚至可以重载为人+人,就看你怎么定义。
这里以复数里的操作符重载函数为例
5.1 成员函数的操作符重载
inline complex& complex::operator += (const complex& r)
{
this->re += r.re;
this->im += r.im;
return *this;
}
5.2 非成员函数的操作符重载
ostream& operator << (ostream& os, const complex& x)
{
return os << '(' << real(x) << ',' << imag(x) << ')';
}
p7 拷贝构造、拷贝赋值、析构
7.1 析构函数
inline String::String (const char* cstr =0)
{
if (cstr) {
m_data = new char[strlen (cstr) + 1];
strcpy (m_data, cstr);
}
else {
//cstr为空
mdata = new char[1];
*mdata = '\0';
}
}
inline string::~String ()
{
delete[] m_data;
}
int main()
{
//这里调用了三次析构函数s1,s2,p
string s1();
string s2("hello");
String* p = new String ( "hello");
delete p;
}
7.2 拷贝构造(copy ctor)
7.2.1 浅拷贝
inline String::String (const string& str)
{
m_data = str.m_data;
}
7.2.2 深拷贝
inline String::String (const string& str)
{
m_data = new char[ strlen (str.m_data) +1 ];
strcpy (m_data, str.m_data);
}
int main()
{
string s1 ( "hello");
string s2 (s1); //括号法
//string s2 = String(s1); //显示法
//string s2 = s1; //隐式转换法
}
7.3 赋值操作符重载(copy op=)
这里为了保证可以连续使用操作符,要传回string类型才能继续使用
而且为了保证可以做左值,必须以引用的形式返回String&
inline String& String::operator= (const string& str)
{
//检测自我赋值(self assignment),这个必须要有
//不只是为了效率问题,还有正确性
if (this == &str)
return *this;
delete[] m_data;
m_data = NULL;
m_data = new char[str1en(str.m_data) + 1];
strcpy(m_data, str.m_data);
return *this;
}
int main()
{
String s1("hello");
String s2(s1);
s2 = s1;
}
p8 堆、栈、内存管理
new的内部实现过程:先分配memory,再调用ctor
Complex* pc = new Comp lex (1,2);
//编译器在内部将其转化为
Complex *pc;
void* mem = operator new (sizeof(Complex)); //分配内存
pc = static_cast<Complex*> (mem); //转换
pc->Complex::Complex(1,2); //构造函数
//Tip: operator new()这个函数,其内部调用malloc(n),在堆区开辟一块空间
delete的内部实现过程:先调用dtor,再释放memory
Complex* pc = new Complex(1,2);
delete pc;
//编译器在内部将其转化为
Complex::~Complex(pc); //析构函数
operator delete(pc); //释放内存
//Tip:operator delete()这个函数其内部调用free(pc)
动态分配单个内存
上下两块红色的表示cookie,记录内存大小方便释放,如64字节的内存空间十六进制就是40
最后一位写成1表示编译器分配给你内存,如果是0表示将内存释放。
总内存大小一定是16的倍数
动态分配array
比上面多了一块记录array个数的空间,如 array[3]
P10 namespace
namespace std
{
}
将代码块封锁,包装在一个单元里面,C++标准库里面所有的东西都包装在std里
可通过下面方式访问
补充:
枚举(enum)
如果一个变量只能有几种可能的值,这样的变量可以定义成枚举类型。所谓的“枚举”是指可以将变量的值挨个列举出来,变量的值只能在列举出来的值的范围内,其他的值对该变量没有意义,例如︰星期几、人种颜色、性别、月份等等。枚举类型也是一种自定义类型。
enum ESex
{
ESex_Male,
ESex_Female
};
注意事项:
①、枚举元素按常量处理,所以称作枚举常量。他们不是变量,所以不要对他们进行赋值
②、枚举元素是常量,但是其也是有值的,他们的值是一个整数,按照元素声明时候的顺序从0开始依次进行+1操作,默认的值就是:0,1,2,3…例如,上面的ESex枚举类型中,ESex_Male的值默认是0,ESex_Female的默认值是1,依此类推。
③、枚举元素有默认的值,但也可以在声明的时候指定值,例如
enum EWeekDay
{
EWeekDay_1 = 3,
EWeekDay_2 = 4,
EWeekDay_3 = 5,
EWeekDay_4, //6
EWeekDay_5, //7
EWeekDay_6 //8
};