C++基础

输出语句

#include <iostream>

int main()
{
    std::cout << "Hello World!\n"; // 输出HelloWorld
    std::cout<<std::endl; // 输出换行
}

如果要使用cout输出变量的地址,最好是使用以下语句,否则cout可能将地址当做字符串处理

std::cout << (void*)&variable << std::endl;

命名空间

#include <iostream>

using namespace std;

int main()
{
    cout << "Hello World!\n"; // 输出HelloWorld
    cout<<endl; // 输出换行
}

变量

变量类型

int、float、double、char、string、bool(unsigned char)

signed:有符号的,可以表示正数和负数

unsigned:无符号的,只能表示正数

如果给bool变量赋值数字,非0将会被转换为1。

#include <iostream>

using namespace std;

int main()
{
    cout << "Hello World!\n"; // 输出HelloWorld
    cout << endl; // 输出换行
    string name; // 姓名
    int age;
    double weight;
    char sex;
    bool flag;
    name = "C Primer Plus";
    age = 18;
    weight = 500;
    sex = 'M';
    flag = true; // bool值在CPP里就是true:1 flase:0

    cout << "name:" << name << "  age:" << age << "  weight:" << weight << "  sex:" << sex << "  flag:" << flag;
    return 0;
}

变量作用域

全局变量和静态局部变量都会被自动初始化为0

输入语句

#include <iostream>

using namespace std;

int main()
{
	cin >> name;
	cout << name << endl;
}

C++11初始化赋值

int a = (15);
int b(20);
int a = {15};
int b{20};

在Linux平台,编译需要加-std=c++11参数

C++11的long long类型

在VS中,long是4字节,32位。 -2147483648~2147483647

在Linux中,long是8字节,64位。 -9223372036854775808~9223372036854775807

C++11标准增了long long类型的整数,至少64位,且至少与long一样长

在VS中,long long是8字节,64位。 -9223372036854775808~9223372036854775807

在Linux中,long和long long类型都是8字节,64位。

ASCII

ASCII控制字符

十进制 符号 中文解释 十进制 符号 中文解释
0 NULL 空字符 16 DLE 数据链路转义
1 SOH 标题开始 17 DC1 设备控制 1
2 STX 正文开始 18 DC2 设备控制 2
3 ETX 正文结束 19 DC3 设备控制 3
4 EOT 传输结束 20 DC4 设备控制 4
5 ENQ 询问 21 NAK 拒绝接收
6 ACK 收到通知 22 SYN 同步空闲
7 BEL 23 ETB 传输块结束
8 BS 退格 24 CAN 取消
9 HT 水平制表符 25 EM 介质中断
10 LF 换行键 26 SUB 替换
11 VT 垂直制表符 27 ESC 换码符
12 FF 换页键 28 FS 文件分隔符
13 CR 回车键 29 GS 组分隔符
14 SO 移出 30 RS 记录分离符
15 SI 移入 31 US 单元分隔符

ASCII显示字符

十进制 符号 中文解释 十进制 符号 中文解释
32 空格 80 P 大写字母 P
33 ! 感叹号 81 Q 大写字母 Q
34 " 双引号 82 R 大写字母 R
35 # 井号 83 S 大写字母 S
36 $ 美元符 84 T 大写字母 T
37 % 百分号 85 U 大写字母 U
38 & 86 V 大写字母 V
39 ' 单引号 87 W 大写字母 W
40 ( 左括号 88 X 大写字母 X
41 ) 右括号 89 Y 大写字母 Y
42 * 星号 90 Z 大写字母 Z
43 + 加号 91 [ 左中括号
44 , 逗号 92 \ 斜线
45 - 减号 93 ] 右中括号
46 . 句点或小数点 94 ^ 音调符号
47 / 反斜线 95 _ 下划线
48 0 数字0的符号 96 ` 重音符
49 1 数字1的符号 97 a 小写字母 a
50 2 数字2的符号 98 b 小写字母 b
51 3 数字3的符号 99 c 小写字母 c
52 4 数字4的符号 100 d 小写字母 d
53 5 数字5的符号 101 e 小写字母 e
54 6 数字6的符号 102 f 小写字母 f
55 7 数字7的符号 103 g 小写字母 g
56 8 数字8的符号 104 h 小写字母 h
57 9 数字9的符号 105 i 小写字母 i
58 : 冒号 106 j 小写字母 j
59 ; 分号 107 k 小写字母 k
60 < 小于 108 l 小写字母 l
61 = 等号 109 m 小写字母 m
62 > 大于 110 n 小写字母 n
63 ? 问号 111 o 小写字母 o
64 @ 电子邮件符号 112 p 小写字母 p
65 A 大写字母 A 113 q 小写字母 q
66 B 大写字母 B 114 r 小写字母 r
67 C 大写字母 C 115 s 小写字母 s
68 D 大写字母 D 116 t 小写字母 t
69 E 大写字母 E 117 u 小写字母 u
70 F 大写字母 F 118 v 小写字母 v
71 G 大写字母 G 119 w 小写字母 w
72 H 大写字母 H 120 x 小写字母 x
73 I 大写字母 I 121 y 小写字母 y
74 J 大写字母 J 122 z 小写字母 z
75 K 大写字母 K 123 { 左大括号
76 L 大写字母 L 124 | 竖线
77 M 大写字母 M 125 } 右大括号
78 N 大写字母 N 126 ~ 波浪号
79 O 大写字母 O 127 删除

转义字符

ASCII码值 转义字符 含义
0 \0 空,给字符型变量赋值时可以直接书写0。
10 \n 换行(LF) ,将当前位置移到下一行开头。
13 \r 回车(CR) ,将当前位置移到本行开头
9 \t 水平制表(HT) (跳到下一个TAB位置)
92 \ 斜线
34 " 双引号,书写字符时不必转义。
39 ' 单引号,书写字符串中不必转义。
7 \a 警报
8 \b 退格(BS) ,将当前位置移到前一列
12 \f 换页(FF),将当前位置移到下页开头
11 \v 垂直制表(VT)

C++11的原始字面量

格式:R"(字符串内容)"

使用R"(string\n)",可以取消字符串内容里包含的转义字符

如果是R"aa(string\n)bb",效果同上

std::cout << R"(string\r\t\n)" << std::endl;

字符串型

C++字符串:string 变量名 = "字符串内容" ;

C字符串:char 变量名[] = "字符串内容" ;

C风格字符串的本质是字符数组,C++风格字符串的本质是类,它封装了C风格字符串。

赋值:变量名 = "字符串内容" ;

拼接:变量名 = 变量名+ "字符串1" + "字符串2" + "字符串的内容n";

字符串比较:支持关系运算符

#include <iostream>
int main()
{
    string str = "123";
    if(str == "123")
        std::cout << "true" << std::endl;
}

如果字符串内容都是常量,则不能使用加号(+)拼接

string str;
str = "123" + "string" + "abc"; // 不能这样使用

如果内容过长,可以多行书写

string str;
str = "123"
    "string""abc";
/*
输出结果为
123stringabc

*/

自动类型转换

  1. 当表达式中出现了不同类型混合运算,取值范围低的类型将会自动向范围高的进行转换
  2. 当表达式中含有浮点型时,将会自动向浮点类型转换
  3. 赋值时,左右操作数类型不一致,右操作数自动转换为左操作数类型,然后赋值
  4. 赋值时,右操作数超出了左操作数的取值范围,将会把右操作数截断后赋值给左操作数

强制类型转换

(类型)表达式

类型(表达式)

double d = 12.59;
int i = (int)d;
int k = int(d);
/*
i和k的值为12
*/

指针

常量指针

声明格式:const type* variable;

  1. 不能通过*variable修改(可以访问)variable指向的内存空间存放的内容,但是可以通过原变量名进行修改
  2. 可以更改variable的内容(可以更改指针的指向)
int a = 3, b = 5;
const int* p = &a;
*p=4; // 不能这样改
a = 4; // 这样可以
p = &b; // ok

常量指针一般用于修饰函数的形参,表示不希望在函数内部修改形参指向的内存空间的内容

指针常量(引用)

声明格式:type* const variable;

  1. variable的值不可以改变,因此声明的时候必须初始化
  2. 可以通过*variable修改variable指向的内存空间的内容

常指针常量(常引用)

声明格式:const type* const variable;

  1. variable的值不可改变,不能通过*variable改变variable指向的内存空间的内容,但是可以访问

指针的输出

有时候C++不能识别出指针的内容是地址,因此可以这样输出(以整型变量的指针为例)

int a;
int* ptr = &a;
std:out << (int*)&a << std::endl;
std:out << (int*)ptr << std::endl;

CPP内存空间

程序运行时,内存主要分为四个区:栈、堆、数据段、代码段

  1. 栈:存放程序的局部变量、函数参数和返回值

  2. 堆:存放动态开辟内存的变量

  3. 数据段:存放程序中的全局变量和静态变量

  4. 代码段:存放可执行程序的二进制代码和常量

  5. 管理方式不同:栈是系统自动管理的,在出作用域时,将自动被释放;堆需手动释放,若程序中不释放,程序结束时由操作系统回收。

  6. 空间大小不同:堆内存的大小受限于物理内存空间;而栈就小得可怜,一般只有8M(可以修改系统参数)。

  7. 分配方式不同:堆是动态分配;栈有静态分配和动态分配(都是自动释放)。

  8. 分配效率不同:栈是系统提供的数据结构,计算机在底层提供了对栈的支持,进栈和出栈有专门的指令,效率比较高;堆是由C++函数库提供的。

  9. 是否产生碎片:对于栈来说,进栈和出栈都有着严格的顺序(先进后出),不会产生碎片;而堆频繁的分配和释放,会造成内存空间的不连续,容易产生碎片,太多的碎片会导致性能的下降。

  10. 增长方向不同:栈向下增长,以降序分配内存地址;堆向上增长,以升序分配内存地址。

动态分配内存

申请内存:new type(initval); // C++11支持{}

new type[size];

释放内存:delete address;

int* p = new int(5);
std::out << *p << std::endl; // 输出5
delete p;
p = NULL;

int* arr = new int[10]; // 申请10个int类型的内存空间
delete arr;
arr = NULL;

对空指针进行delete不会出错,系统会忽略该操作,内存释放后,应该将指针指向NULL

空指针

指针被delete之后,应该赋值为空指针NULL0 ,C++11建议使用nullptr表示空指针,也就是(void*)0

Linux下如果使用nullptr,需要加-std=c++11参数

野指针

指向未分配的内存空间的指针

规避野指针:

  1. 定义指针时,初始化为nullptr
  2. 使用delete释放后,赋值为nullptr
  3. 函数不返回局部变量地址

函数指针

函数的地址就是函数在内存中的起始地址

使用函数指针

  1. 声明函数指针
  2. 让函数指针指向函数的地址
  3. 通过函数指针调用函数

函数指针的声明和调用

声明格式:retval_type(*funptr)(type1, type2, typen);

C++调用:funptr(type1_val, type2_val, typen_val);

C调用:(*funptr)(type1_val, type2_val, typen_val);

一维数组

数组的声明

语法:type array[length];

length必须是整数,可以是常量,也可以是变量和表达式

C90规定必须用常量表达式指定数组大小,C99允许使用整型非常量表达式,Linux中可以使用变量

数组占用内存

使用sizeof(array)函数可以得到数组占用空间大小(字节),但是只适用于C++的基本数据类型

数组的初始化

type array[length] = { val1, val2, val3, valn };

type array[] = { val1, val2, val3, valn };

type array[length] = { 0 };

type array[length] = {};

C++11可以不写等号

清空数组

void *memset(void *_Dst, int _Val, size_t _Size);

Linux下使用需要包含string.h头文件

复制数组

void *memcpy(void *_Dst, const void *_Src, size_t _Size);

Linux下使用需要包含string.h头文件

数组名是地址

数组名不一定会被解释为地址,使用sizeof(array)时,返回array数组占用的内存字节数

数组名是常量,不可更改

数组名作为函数的参数

使用数组名/数组首地址作为函数的形参,函数有两种定义方式

retval_type fun(type* array_ptr);

retval_type fun(type array_ptr[]);

在函数内部,不要对指针array_ptr使用sizeof函数,它不是数组名

使用new动态创建一维数组

语法:type* array_ptr = new type[length];

释放:delete[]array_ptr

声明普通数组的时候,数组长度可以用变量,相当于在栈中动态创建数组,并且不需要释放

如果内存不足,调用new会产生异常,导致程序中止,如果在new后面加(std::nothrow)则返回nullptr,不会产生异常,之后可以对指针判空,避免异常导致的崩溃

使用delete[]释放,不需要指定数组大小,系统会自动跟踪已经分配数组的内存

数组的排序

void qsort(void *_Base, size_t NumOfElements, size_t _SizeOfElement, _CoreCrtNonSecureSearchSortCompareFunction _CompareFunction );

  1. 数组起始地址

  2. 数组元素个数size_tunsigned long long

  3. 单个数组元素占用字节数

  4. 回调函数地址,回调函数应该符合int compare(const void* p1, const void* p2)

    1. 如果compare函数的返回值<0,那么p1所指向元素会被排在p2所指向元素的前面

    2. 如果函数返回值==0,那么p1和p2的排序顺序不确定

    3. 如果函数返回值>0,那么p1所指向的元素会被排在p2所指向的元素的后面

      int compare(const void* p1, const void* p2)
      {
          return *((int*)p1) - *((int*)p2); // 升序
          return *((int*)p2) - *((int*)p1); // 降序
      }
      

多维数组作为函数的参数

行指针

type (*ptr)[size]; // size is length

行指针是二维数组每行的首元素地址,每行有size个元素

int array1[3][4]; // array1是数组长度为3的整型数组的行地址
int (*p)[4] = array1; // p是每行首元素地址,每行有4个元素

int array2[2][3][4]; // array2是[3][4]二维数组的二维地址
int (*p)[3][4]; // p是每行首元素地址,每个元素都是一个[3][4]的二维数组

将多维数组传递给函数

// len是行地址的个数(数组有多少行)
void fun(int (*p)[3], int len);
void fun(int [][3], int len);

结构体

定义语法:

struct struct_name
{
    member1_type member1;
    member2_type member2;
    membern_type membern;
};
struct struct_name
{
    member1_type member1;
    member2_type member2;
    membern_type membern;
}alias; // alias为结构体struct_name的别名,声明结构体时可以使用alias struct_variable;
struct struct_name
{
    member1_type member1;
    member2_type member2;
    membern_type membern;
}*alias_ptr;// alias_ptr为结构体struct_name*的别名,声明结构体时可以使用alias_ptr struct_variable;
  1. 结构体名是标识符
  2. 结构体成员可以是任意数据类型
  3. 定义结构体的代码可以放在任意位置(包括头文件)
  4. 结构体成员可以使用C++的类,但是不提倡
  5. C++中结构体中可以有函数指针,但是不提倡
  6. C++11中,定义结构体可以指定缺省值

结构体类型变量声明

struct struct_name struct_variable = { member1_val, member2_val, membern_val };
  1. C++11可以不写等于号
struct struct_name struct_variable = { 0 };
// C++11标准写法
struct_name* struct_variable;
*(struct_variable) = { member1_val, member2_val, membern_val };
// 定义的同时初始化
struct_name* struct_variable = new struct_name({ member1_val, member2_val, membern_val });
  1. 在C++中struct可以不写

  2. 定义结构体的时候可以创建结构体变量

    struct struct_name
    {
        member1_type member1;
        member2_type member2;
        membern_type membern;
    };
    
  3. 使用sizeof(结构体变量/结构体类型)函数可以得到结构体变量占用的字节大小,结构体变量占用的空间不一定是各个成员变量的占用空间之和,因为存在字符对齐的情况

    #pragma pack(Bytes) // 如果Bytes为1,那么内存之间就没有空隙了,结构体变量占用空间必定为成员变量占用空间之和
    

    每个编译器的内存对齐的规则不一样,在VS中缺省对齐是8 Bytes

清空结构体

void *memset(void *_Dst, int _Val, size_t _Size);

void bzero(void *s, size_t n);

复制结构体

可以使用memcpy()函数(只适用于C++基本数据类型)

也可以直接使用等于号(只适用于C++基本数据类型)

结构体中的指针成员变量

如果结构体中的指针指向的是动态分配的内存地址

  1. 对结构体变量使用sizeof()函数是无意义的
  2. 对结构体使用memset()可能造成内存泄露

string类中有一个是指向动态分配的内存地址的指针的

struct string
{
    char* ptr; // 指向动态分配的内存地址
}

共用体

  1. 共用体能够存储不同的数据类型,但是同一时间只能存储其中的一种类型
  2. 共用体占用内存的大小是其最大的成员占用内存大小
  3. 全部成员使用一块内存
  4. 匿名共用体没有名字,匿名共同体在声明的时候就要创建变量

声明语法

union union_name
{
    member1_type member1;
    member2_type member2;
    membern_type membern;
};

枚举

  1. 枚举默认情况下,第一个常量的值为0,后面的依次递增

声明语法

enum enum_name
{
    enum_val1, // 0
    enum_val2, // 1
    enum_val3, // 2
    enum_val4 = 5, // 5
    enum_val5, // 6
    enum_val6 // 7
}

可以使用enum_name创建枚举变量,该变量的值,只能是枚举类型包含的常量值

enum_name enum_variable = enum_val5;

可以将整数强制转换为枚举量

enum_name(integer_val)

引用

引用是C++所特有的概念

  1. 引用变量是C++新增的复合类型。
  2. 引用是已定义的变量的别名,因此可以如同使用变量一样使用引用
  3. 引用的主要用途是用作函数的形参和返回值。
  4. 引用的数据类型要与原变量名的数据类型相同。
  5. 必须在声明引用的时候初始化,初始化后不可改变。如果在初始化之后,再对引用赋值新的变量,其地址也不会改变,只会改变引用对应的变量的内容
  6. C和C++用&符号来指示/取变量的地址,C++给&符号赋予了另一种含义。

声明引用

type& reference_name = primary_variable;

使用引用

int primary_variable = 147;
int& reference_name = primary_variable;

std::cout << reference_name << std::endl;
std::cout << primary_variable << std::endl;
std::cout << &reference_name << std::endl;
std::cout << &primary_variable << std::endl;
int new_variable = 5;
reference_name = new_variable; // 相当于把new_variable的值赋给了reference_name或primary_variable
std::cout << &reference_name << std::endl; // 地址为primary_variable的地址
std::cout << &new_variable << std::endl; // 地址与reference_name和primary_variable的地址不同
std::cout << reference_name << std::endl; // 5
std::cout << primary_variable << std::endl; // 5

引用作为函数的参数

传递引用相当于传地址,但是使用引用比使用地址更好理解,引用形参对应的实参必须是变量

void fun(int& rv)
{
	std::out << rv << std:endl;
}
int main()
{
    int pv = 5;
    fun(pv); // 将变量传进去
}

引用作为函数形参

如果一定要向引用传递常量,可以使用const关键字

void fun(const int& rv)
{
	std::out << rv << std:endl;  
}
int main()
{
    fun(5); // 将常量5传进去,形参声明时使用了const关键字,编译器将会为常量5创建一个临时变量,引用的地址将会指向这个临时变量
}

使用const声明引用,并且赋值为常量,编译器将会为这个常量创建一个临时变量,引用的地址将会指向这个临时变量

const int& rv = 5;
// 等同于下面两句
int temp = 8;
const int& rv = temp;

引用作为函数返回值

函数的返回值一般会被拷贝到寄存器或者栈中,然后调用该函数的函数再使用这个值

引用作为函数返回值时

  1. 可以返回函数的引用形参、类的成员、全局变量、静态变量,不可以返回本函数局部变量的引用
  2. 返回引用的函数名是被引用变量的别名,将const用于引用的返回类型
  3. 返回类成员变量的引用时,可以使用const关键字声明接收返回值的引用,避免类成员变量被修改
// 返回函数的引用形参
int& fun2(int &rv)
{
    rv += 1;
	return rv;
}

int main()
{
    int pv = 5;
    int&rv = pv;
    int& vv = fun2(rv);
	std::cout << vv << std::endl;
}

// 返回静态变量
int& fun3(void)
{
    static int sv = 4;
	return sv;
}

int main()
{
    int& rv = fun2();
	std::cout << vv << std::endl;
}

// 函数名其实也是引用变量的别名
int& fun3(void)
{
    static int sv = 4;
    sv += 1;
    std::cout << sv << std::endl; // 第一次输出5,第二次输出6
	return sv;
}

int main()
{
    int& rv = fun3();
    fun3() = 10; // 调用一次fun3(),输出6,并且fun()是sv的引用,因此这一句也给sv赋值了10
	std::cout << vv << std::endl; // 输出10
}

函数形参的默认值

可以使用如下语法声明函数的默认参数

void function(int a, char b = 'x', string c = "123456")
{
    
}

为形参指定默认值时,要注意以下几点

  1. 指定默认值的形参一定要在形参列表的末尾部分
  2. 不允许指定默认值的形参后面还有普通形参
  3. 调用函数时,必须为前n个形参传递实参,n(0~n)

函数重载

函数的重载其实就是函数多态

  1. 可以定义多个同名函数,他们的形参、形参的数据类型、形参的排序可以不同

  2. 使用重载函数,编译器将会自动匹配实参对应类型(可能会自动类型转换:低范围转换为高范围类型)的函数,如果有多个重载函数可以匹配,将会报错

  3. 重载函数只是在编写代码时看起来是同名的,实际上编译器在编译过程中会为重载函数重新命名,因此重载函数实际上并不是同名的

  4. 如果两个重载函数的形参列表中对应位置有同类型的变量和引用,如果实参是常量,将会匹配形参为变量的函数;如果实参是变量,这两个重载函数都会被匹配,此时编译出错

    fun(int a){}
    fun(int& a){}
    int main()
    {
        int k = 4;
        fun(2); // 匹配第一个fun
        fun(k); // 两个都匹配
    }
    
  5. 如果重载函数的形参列表中含有指定了默认值的形参,那么也可能出错

    fun(int a, int b = 2, int c = 3){}
    fun(int a, int b = 3){}
    fun(int a){}
    int main()
    {
        fun(1); // 三个fun()都匹配
        fun(1, 2); // 匹配前两个fun()
        fun(1, 2, 3); // 匹配第一个fun()
    }
    
  6. 如果仅对同名函数的形参使用const用于区分,则不能成为重载函数

    // 这两者实际上还是一个函数,函数重名冲突
    fun(const int a){}
    fun(int b)
    

内联函数(inline)

  1. 内联函数相当于把函数的函数体嵌入到调用其的函数中,调用几次嵌入几次

  2. 内联函数可以提高程序的运行速度(不再需要将实参和函数的地址拷贝到堆栈中了),但是比较占用内存

  3. 普通函数的声明和定义可以分开,内联函数的声明和定义是在一起的

  4. 如果内联函数过大,编译器将不会将其作为内联函数

  5. 内联函数不能递归

inline void fun()
{
    int a = 1;
    char b = 2;
    std::cout << a << b << std::endl;
}
int main()
{
    fun();
    fun();
}
// 上面的main函数相当于下面的main函数
int main()
{
    {
        int a = 1;
        char b = 2;
        std::cout << a << b << std::endl;
    }
    {
        int a = 1;
        char b = 2;
        std::cout << a << b << std::endl;
    }
}

类的声明语法

class Class_name
{
public:
    // 类的成员变量,也叫属性
    member1_type member1; 
    member2_type member2;
    membern_type membern;
    // 类的成员函数,也叫方法,可以定义在类的外面
    retval_type func1(type formal_param1, type formal_param2, type formal_paramn){}
    retval_type func2(type formal_param1, type formal_param2, type formal_paramn){}
    retval_type funcn(type formal_param1, type formal_param2, type formal_paramn){}
    // 在类外定义的成员函数的声明语句
    retval_type funcn_1(type formal_param1, type formal_param2, type formal_paramn);
private:
protected:
};
// 在类外面定义成员函数,而类中必须要有一个对应的声明语句
retval_type Class_name::funcn_1(type formal_param1, type formal_param2, type formal_paramn)

类的访问权限

类有三种访问权限,对应三个关键字

public:类外可访问

private:只有本类的成员函数可访问

protected:用于类的继承

  1. 类的定义中publicprivate可以出现多次
  2. 结构体的成员默认为public,类的成员默认为private

构造函数和析构函数

构造函数可以在对象实例化时,自动进行初始化(初始化属性)

析构函数可以在销毁对象前,自动进行清理(释放动态内存)

如果不提供构造函数和析构函数,编译器会提供空函数体的构造函数和析构函数

构造函数的定义

  1. 构造函数的访问权限必须是public
  2. 函数名与类名相同
  3. 无返回值,不用写void
  4. 可以有形参,可以重载,形参可以有默认值
  5. 没有形参的构造函数是默认构造函数,定义重载构造参数前,必须定义一个默认构造参数
  6. 创建对象时会自动调用,不能人为调用
  7. 在类内部使用构造函数后加括号Class_name();不是调用构造函数,而是创建匿名对象
  8. 如果成员变量中有指针,构造函数内一定要把指针初始化为nullptr,不然析构函数对其进行delelte时,可能delete野指针,导致程序崩溃
Class_name()
{
    //TODO
}

析构函数的定义

  1. 析构函数的访问权限必须是public
  2. 函数名为~Class_name
  3. 无返回值,不用写void
  4. 无形参,不能重载
  5. 销毁对象前自动调用一次,可以人为调用

类的使用

  1. 类的成员函数之间可以相互调用,也可以递归

  2. 成员函数可以重载

  3. 成员函数的形参可以使用默认值

  4. 类指针的用法和结构体指针用法相同

  5. 类的成员可以是任意类型

  6. 可以为类的属性指定默认值(C++11)

  7. 可以创建对象数组,如同其他类型的数组

  8. 对象可以作为实参传递给函数,一般传递引用

  9. 可以使用newdelete实例化和销毁对象,也会自动调用构造和析构函数

  10. 在类的外部,一般不读写类的属性,而是使用成员函数对类的属性进行读写

  11. 对象一般不使用memset()清空属性,可以写一个专门清空属性的成员函数

  12. 一般不对类/对象使用sizeof(可以使用)

  13. 使用结构体描述纯粹的数据,用类描述对象

  14. 如果在类中使用枚举,枚举的作用域是整个类,这样可以避免定义过多的常量

  15. 在类中定义的成员函数,都将自动成为内联函数,类外定义的如果没有inline关键字则不是内联函数

    retval_type Class_name::funcn_1(type formal_param1, type formal_param2, type formal_paramn) // 不是内联
    inline retval_type Class_name::funcn_1(type formal_param1, type formal_param2, type formal_paramn) // 是内联
    
  16. 为了区分属性和成员函数的形参,在属性名前加m_前缀或_前缀

    m_member1_type member1;
    _member1_type member1;
    
  17. 一般将声明类的代码放在头文件中,把成员函数定义的代码放在源文件中

  18. 不建议在构造函数/析构函数中写太多的代码,初始化成员变量的语句可以写在某个成员函数里

  19. 构造函数中最好只写初始化语句,或只会成功而不会失败的语句

  20. 如果类的成员也是类,那么应该先构造成员类,先析构成员类

class Class_name
{
public:
    Class_name(){} // 必须定义一个无参数的构造函数(默认构造函数)
    Class_name(int param)
    {
        
    }
    ~Class_name() // 析构函数
    {
        
    }
    void fun()
    {
        
    }
};

// 隐式调用构造函数
Class_name cls;
Class_name cls();
Class_name cls(_member_val);

// 显示调用构造函数
Class_name cls;
cls = Class_name(); // Class_name()创建匿名对象,这一句将匿名对象的内容赋值给了对象cls
Class_name cls = Class_name(_member_val);

// 只有一个形参的构造函数,允许其使用赋值语句将其初始化为一个值
Class_name cls = 10 // param = 10

// 动态分配对象
Class_name* cls = new Class_name; // 实例化对象,会自动调用构造函数
cls->fun();
delete cls; // 销毁对象,会自动调用析构函数
posted @ 2022-12-27 04:06  HelliWrold1  阅读(74)  评论(0编辑  收藏  举报