0. 前言:类的分类方法(2种)

对于基于对象编程,类之间还没有建立关系,类看成是单一的class,所以是基于对象的编程
对于成员属性中是否含有指针
complex(class without pointer)
不带指针,创建的对象里放的是具体的数据,不带指针的类多数不用写析构函数

string(class with pointer)
带指针,创建的对象里是放指针的

在创建对象时,编译器会给一套默认的拷贝构造、拷贝赋值以及析构函数,编译器给的函数操作只是简单的一个字节一个字节复制,所以当数据为指针时,就需要自己重写这些函数,不然复制过来的只是地址,那么他们就会指向同一块内存。

p2. 头文件与类声明

像c语言这种面向过程的语言,参数大多是全局的,由于提供的关键字不多,所以参数可供函数之间随便使用。
面向对象语言就是把参数和函数包在一起,你的数据只有你这个函数可以使用,其他函数看不见,并提供了许多关键字,如private,一般定义的属性都是私有的权限。
C++中也有结构体struct,可以理解为class,其与c中结构体主要区别就是默认访问权限的问题,c语言默认是public,c++默认权限是private。

2.1 头文件

image.png

2.2 inline内联函数

在类内声明,类内实现的方法称为内联函数,好处是调用速度比较快,
但最终决定这个函数是不是inline由编译器决定,如果函数相对来说比较复杂就不认为是inline函数

p3 普通构造函数

特点: 1.当你创建一个对象时,构造函数会自动调用
2.函数名与类名必须相同
3.可以有参数,且参数可以有默认值
4.没有返回值
5.初始化列表(initialization list)
6.注意构造函数的重载

tip:在单例模式(Singleton)下特殊点是把构造函数放在private里面

p4. 参数传递与返回值

总结:

  1. 数据一定放在private里
  2. 参数尽量以reference形式来传,要不要加const看状况
  3. 返回值首先考虑用reference来传,如果不行采用其他方式,主要就是看函数执行的结果放到什么地方去,如果是local object,就不能用reference来返回;如果函数执行结果的目的端本来就存在,那么就&来返回
  4. 在类中的函数参数需要加const就必须加

QQ截图20210826192504.png

p5. 操作符重载

操作符其实可以理解成一个函数,在数学中或者其他语言中如(C语言),加减乘除的符号就只是简单运算,但为了方便使用,c++允许编程的人自己去设计一种运算方式,你甚至可以重载为人+人,就看你怎么定义。
这里以复数里的操作符重载函数为例

5.1 成员函数的操作符重载

  1. inline complex& complex::operator += (const complex& r)
  2. {
  3. this->re += r.re;
  4. this->im += r.im;
  5. return *this;
  6. }

5.2 非成员函数的操作符重载

ostream&  operator << (ostream& os, const complex& x)
{
    return os << '(' << real(x) << ',' << imag(x) << ')';
}

p7 拷贝构造、拷贝赋值、析构

image.png
QQ截图20210827093930.png

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 浅拷贝

image.png

inline String::String (const string& str)
{
    m_data = str.m_data;
}

7.2.2 深拷贝

image.png

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;
}

image.png

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的倍数
image.png
动态分配array
比上面多了一块记录array个数的空间,如 array[3]
image.png

P10 namespace

namespace std
{

}

将代码块封锁,包装在一个单元里面,C++标准库里面所有的东西都包装在std里
可通过下面方式访问
image.png

补充:
枚举(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
};