C++备忘录(持续更新)

类(class)和结构体(strut)的区别

类(class):默认访问权限为private

结构体(strut):类的一种特殊情况,默认访问权限为public

函数设置参数默认值的注意点

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#include <iostream>

using namespace std;

void fun(int a = 12, char c = 'a') //全部指定默认值
{
cout << a << endl;
cout << c << endl;
}

void fun2(int a, int b = 2, char c = 'a') //部分指定默认值,必须 从右向左 连续 指定
{
cout << a << endl;
cout << b << endl;
cout << c << endl;
}

void fun3(int a = 1); //声明函数原型要在这里赋默认值,下面不用再赋默认值

int main()
{
fun();
fun2(1);
fun3();
return 0;
}

void fun3(int a)
{
cout << a << endl;
}

函数重载函数设置参数默认值 同时使用时,容易造成函数调用不明确。

malloc(free) 和 new(delete) 的区别

malloc:申请对象空间,不会触发对象的构造函数

new:申请对象空间,会触发对象的构造函数

free:释放对象空间,不会触发对象的析构函数

delete:释放对象空间,会触发对象的析构函数

const关键字

  1. const 类成员初始化

(a)成员只有 const, 类似引用, 需要在 初始化列表 进行初始化(特殊情况C+11特性, 如果是整数(char, short, int), 可以直接在声明处赋值)。

(b)成员既有 const 又有 static, 按照 static 初始化方式。(静态成员如果是整数(char, short, int), 可以直接在声明处赋值,或者在定义处初始化)

  1. const 重载

(a)类的成员函数后面加 const,表明这个函数不会对这个类对象的数据成员(准确地说是非静态数据成员)作任何改变。

​ 在设计类的时候,一个原则就是对于不改变数据成员的成员函数都要在后面加 const,而对于改变数据成员的成员函数不能加 const。所以 const 关键字对成员函数的行为作了更加明确的限定:有 const 修饰的成员函数(指 const 放在函数参数表的后面,而不是在函数前面或者参数表内),只能读取数据成员,不能改变数据成员;没有 const 修饰的成员函数,对数据成员则是可读可写的。

(b)类中函数只有 const 属性不同,可以进行函数重载

​ 我们知道,可以通过指针或者引用的 const 形参进行函数重载。类的const 成员函数重载也是一样的,const 修饰的是 this 指针。

  1. const 类对象

​ const 类对象只能访问 const 成员

  1. const 函数改变成员变量

​ 可以使用 mutable 修饰类成员变量,这样在 const 函数的内部可以改变成员变量。

  1. const 返回值

​ 对返回值使用const有可能提高一个函数的安全性和效率,否则还会出问题。

static 关键字

  1. static成员变量必须在类声明的外部进行初始化。(静态常量整型数据成员可以直接在类声明时,进行初始化)
  2. static成员变量和普通static变量一样,都在内存分区的全局数据区分配内存,到程序结束后释放。这就意味着,static 成员变量不随对象的创建而分配内存,也不随对象的销毁而释放内存。而普通成员变量在对象创建时分配内存,在对象销毁时释放内存。

构造函数与析构函数

拷贝构造函数
  1. 拷贝构造函数的声明和调用情况

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    #include <iostream>

    using namespace std;

    class CTest
    {
    public:
    CTest()
    {
    cout << "构造函数" << endl;
    }

    CTest(const CTest& ct) //拷贝构造函数声明
    {
    cout << "拷贝构造函数" << endl;
    }
    };

    CTest fun()
    {
    CTest test; //调用构造函数
    return test; //返回时生成临时对象,调用拷贝构造函数。(相当于 CTest temp = CTest(test);)
    }

    int main()
    {
    CTest test1; //调用构造函数
    CTest test2(test1); //调用拷贝构造函数
    CTest test3 = test1; //调用拷贝构造函数
    CTest test4 = CTest(test1); //调用拷贝构造函数
    CTest* test5 = new CTest(test1); //调用拷贝构造函数

    fun(); //函数返回值生成临时对象过程中,调用拷贝构造函数
    return 0;
    }
  1. 浅拷贝与深拷贝

    当出现类的等号赋值时,会调用拷贝函数,在未定义显示拷贝构造函数的情况下,系统会调用默认的拷贝函数——即浅拷贝,它能够完成成员的一一复制。当数据成员中没有指针时,浅拷贝是可行的。但当数据成员中有指针时,如果采用简单的浅拷贝,则两类中的两个指针将指向同一个地址,当对象快结束时,会调用两次析构函数,而导致指针悬挂现象。所以,这时,必须采用深拷贝。

    浅拷贝出现的问题:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    #include <iostream>

    using namespace std;

    class CStu
    {
    public:
    int* a;

    CStu()
    {
    a = new int[2];
    a[0] = 12;
    a[1] = 13;
    }

    ~CStu()
    {
    delete[] a;
    }
    };

    int main()
    {
    CStu stu;
    cout << stu.a[0] << " " << stu.a[1] << endl;

    CStu stu2 = stu;
    cout << stu2.a[0] << " " << stu2.a[1] << endl;

    return 0;
    } //多次释放同一块空间

    深拷贝解决问题:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    #include <iostream>

    using namespace std;

    class CStu
    {
    public:
    int* a;

    CStu()
    {
    a = new int[2];
    a[0] = 12;
    a[1] = 13;
    }

    //深拷贝
    CStu(const CStu& stu)
    {
    //申请指针空间
    this->a = new int[2];
    //this->a[0] = stu.a[0];
    //this->a[1] = stu.a[1];
    memcpy(this->a,stu.a,2*sizeof(int));
    }

    ~CStu()
    {
    delete[] a;
    }
    };

    int main()
    {
    CStu stu;
    cout << stu.a[0] << " " << stu.a[1] << endl;

    CStu stu2 = stu;
    cout << stu2.a[0] << " " << stu2.a[1] << endl;

    return 0;
    }

    深拷贝与浅拷贝的区别就在于深拷贝会在堆内存中另外申请空间来储存数据,从而也就解决了指针悬挂的问题。简而言之,当数据成员中有指针时,必须要用深拷贝

  1. 一个类中可以存在多于一个的拷贝构造函数吗?

    答:类中可以存在超过一个拷贝构造函数。

    1
    2
    3
    4
    5
    class X { 
    public:
    X(const X& a); // const 的拷贝构造
    X(X& a); // 非const的拷贝构造
    };

常规函数与内联函数

内联函数:空间换时间。函数代码少,流程直接,调用频繁,可以使用内联函数。内联函数不能递归。类内定义的函数都是内联函数。

常规函数:时间换空间。函数代码多,多层嵌套循环,建议使用常规函数。

单例模式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
#include <iostream>
using namespace std;

class CFather
{
private:
//1. 将构造函数访问权限设为 private,使类外不能直接实例化对象
CFather()
{

}
public:
//2. 定义一个静态标记,记录是否已经创建对象,类外进行初始化
static int Cflag;
//3. 定义一个静态函数,通过静态函数申请对象空间,并返回地址
static CFather* CreateOJ()
{
if (Cflag == 0)
{
Cflag = 1;
return (new CFather);
}
else
{
return NULL;
}
}
//4. 定义析构函数将标记清空,以达到重复申请对象目的
~CFather()
{
Cflag = 0;
}
};
int CFather::Cflag = 0;
int main()
{
CFather* f = CFather::CreateOJ();
delete f;
CFather* f1 = CFather::CreateOJ();
delete f1;
return 0;
}

指针、引用、指针引用、二级指针区别

传递指针时,只可以改变指针所指的内容,不可以改变指针本身(引用同理)

传递指针引用时,既可以改变指针所指的内容,又可以改变指针本身(二级指针同理)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
#include<iostream>
using namespace std;
void fun1(int *a)
{
*a = 1;
// 传指针时,不能改变函数外原指针所指地址
}

void fun2(int &a)
{
a = 2;
// 引用只能初始化一次,后续不能改变引用所指空间
}

void fun3(int **a)
{
**a = 3;
*a = new int(33);
// 二级指针即可以改变所指一级指针所指的内容,又可以改变所指一级指针的地址。指针传递的本质是值传递。
}

void fun4(int *&a)
{
*a = 4;
a = new int(44);
// 指针引用既可以改变指针所指的内容,又可以改变指针所指的地址。优点:比二级指针省空间,安全
}
int main()
{
int a=0;

int *b;
b = &a;
fun1(b);
cout<<"fun1(): "<<a<<" "<<*b<<endl;

int &c = a;
fun2(c);
cout<<"fun2(): "<<a<<" "<<c<<endl;

int **d; //二级指针定义
d = &b;
fun3(d);
cout<<"fun3(): "<<a<<" "<<*b<<" "<<**d<<endl;

b = &a;
fun4(b); //指针引用 ,只用传一级指针就行了
cout<<"fun4(): "<<a<<" "<<*b<<endl;
return 0;
}
坚持原创技术分享,您的支持将鼓励我继续创作!