抱歉,您的浏览器无法访问本站

本页面需要浏览器支持(启用)JavaScript


了解详情 >

类型与变量

类型 变量 最小位数
int a 16
long long b 64
char c 8
double d N/A
float e N/A
1
2
3
4
5
int a;
long long b;
char c;
double d;
float e;

$$
类型 = 数据类型 + 数据操作
$$

类与对象

$$
数据 + 行为 = 封装
$$

什么是面向对象?

  1. 面向对象是一种编程思想, 把一切东西看出一个个对象,比如人、耳机、鼠标、水杯等… …他们各自都有属性,比如耳机是白色的,鼠标是黑色的等等,把这些对象拥有的属性变量和操作这些属性的函数打包成一个类来表示

  2. 面向过程和面向对象的区别

    面向过程: 根据业务逻辑从上到下写代码。

    面向对象: 将数据与函数绑定到一起,进行封装, 这样能够更快速的开发程序,减少了重复代码的重写过程

类(类型) 对象(变量)
Cat garfield
Dog odie
People qz
1
2
3
4
5
6
class Cat {  
};
class Dog {
};
class People {
};
1
2
3
Cat garfield;
Dog odie;
People qz;

成员属性方法

1
2
3
4
5
6
7
8
9
10
class People {
// 成员属性
string name;
Day birthday;
double height;
double weight;
// 成员方法
void say(string word);
void run(Location &Ioc)
};

访问权限

数据安全性

描述
public 公共访问权限
private 私有访问权限
protected 受保护的访问权限
friend 友元

访问权限就相当于类外对访问类内部的属性和方法的时候才起到作用

public : 类外部可以随意访问的

private : 类外部是访问不了的

protected : 只有子类才能访问的

friend :隔壁老王

1
2
3
4
5
6
7
8
9
class People {

public :
string name();
string birthdat();
double heig
private :

};

命名空间

命名空间可以:

  • 嵌套
  • 追加
  • 声定(声明定义)分离

命名空间的定义

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
/*************************************************************************
> File Name: class.cpp
> Author: 秃头王
> Mail: 1658339000@qq.com
> Created Time: 2022年07月12日 星期二 17时39分32秒
************************************************************************/

#include <iostream>
// 前期 不建议使用命名空间
using namespace std;

// 定义一个命名空间ttw - 告诉编译器我想把a,b打包的命名空间中(变量、函数)
namespace ttw {
int a, b;
} // end of namespace ttw

namespace qz {
int a, b;
} // end of namespace qz

int main() {
// 访问命名空间 ttw 中的 a、b
ttw::a = ttw::b = 1;
qz::a = qz::b = 2;
cout << ttw::a << qz::a << endl;
return 0;
}

去掉 using namespace ***;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <iostream>

using namespace ttw;

// 定义一个命名空间ttw - 告诉编译器我想把a,b打包的命名空间中(变量、函数)
namespace ttw {
int a, b;
} // end of namespace ttw

namespace qz {
int a, b;
} // end of namespace qz

int main() {
// 访问命名空间 ttw 中的 a、b
// ttw::a = ttw::b = 1;
a = b = 1;
qz::a = qz::b = 2;
cout << a << qz::a << endl;
return 0;
}

命名空间嵌套

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <iostream>

using namespace ttw;

namespace ttw {
int a = 1;
namespace qz {
int a = 2;
} // end of namespace qz
} // end of namespace ttw

int main() {
cout <<"ttw 中的 a : " << ttw::a << endl; // out 1
cout <<"q z 中的 a : " << ttw::qz::a << endl; // out 2
return 0;
}

命名空间是开放的,既可以随时把新的成员加入已有的命名空间中(向命名空间中追加)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
#include <iostream>

using namespace ttw;

namespace ttw {
int a = 1;
} // end of namespace ttw

namespace ttw {
int b = 2;
} // end of namespace ttw

int main() {
cout << ttw::a << endl; // out 1
cout << ttw::b << endl; // out 2
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
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
/*************************************************************************
> File Name: 2.class.cpp
> Author: 秃头王
> Mail: 1658339000@qq.com
> Created Time: 2022年07月12日 星期二 17时50分29秒
************************************************************************/

#include <iostream>
using namespace std;

#define BEGINS(x) namespace x {
#define ENDS(x) } // end of namespace x

BEGINS(TEST1)

// 内存大小 对齐方式
class A_with_int {
// 只有一个 int sizeof(A_with_int) = 4
int a;
// a 和 b 都有就占 16
double b;

// 数据区、方法区

// a 和 b加上函数, 大小是不变的 因为存放的区域的不同
void say();
};

class People {

public :
// 成员属性
string name;
int age;
double height;
double weight;

// 成员方法
// 行为 说话
void say(string name);
/*
{
// 声明 + 定义 正常工程是拆开的
// this 是一个特殊的变量
cout << "my first name is" << this->name << endl;
cout << "my second name is " << name << endl;
return ;
}
*/

void run();

};

/*

// 类外定义
void People::say(string name) {
cout << "my first name is" << this->name << endl;
cout << "my second name is " << name << endl;
return ;
}
*/

ENDS(TEST1)

// 命名空间外定义
void TEST1::People::say(string name) {
cout << "my first name is" << this->name << endl;
cout << "my second name is " << name << endl;
return ;
}

int main() {

cout << "class A int " << sizeof(TEST1::A_with_int) << endl;

TEST1::People ttw;
TEST1::People laojia;
TEST1::People jgouba;
TEST1::People jdp;
ttw.name = "Tu tou wang";
laojia.name = "Lao jia";
jgouba.name = "Jia gou ba";
jdp.name = "Jia da pao";

/*
ttw.say();
laojia.say();
jgouba.say();
jdp.say();
*/

ttw.say("TTW");
laojia.say("LJ");
jgouba.say("JGB");
jdp.say("JDP");

return 0;
}

类对象的 大小问题

  • sizeof() 算的是什么?

​ 相关类型在数据区中的存储空间

  • 为什么sizeof() 计算时候只关注数据的大小

​ 因为方法区的大小不会随着对象的曾加而增加

  • sizeof() 只计算和对象数量成正比的数据区的大小

实现自己的cout、cin

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
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
/*************************************************************************
> File Name: 3.class.cpp
> Author: 秃头王
> Mail: 1658339000@qq.com
> Created Time: 2022年07月12日 星期二 20时35分16秒
************************************************************************/

#include <iostream>

#define BEGINS(x) namespace x {
#define ENDS(x) } // end of namespace x

BEGINS(ttw)

class istream {

public :
istream &operator>>(int &x);

};


istream &istream::operator>>(int &x) {
scanf("%d", &x);
return *this;
}

istream cin;

// cout
class ostream {

public :
ostream &operator<<(int x);
ostream &operator<<(char ch);

};


ostream &ostream::operator<<(int x) {
printf("%d",x);
return *this;
}

ostream &ostream::operator<<(char ch) {
printf("%c", ch);
return *this;
}


ostream cout;

const char endl = '\n';


ENDS(ttw)


int main() {
int n = 123;
// cout 本质是一个对象
std::cout << n << std::endl;

// 自己的 cout
ttw::cout << n << ttw::endl;
// 自己的 cin
ttw::cin >> n;
ttw::cout << n << ttw::endl;
return 0;
}

为什么重载左移运算符要返回引用?

是为了连续输出

比如 1 + 2 + 3 + 5

先 1 + 2 = 3

3(用的是1 + 2的结果的返回值) + 3

这是为了实现连续加

// cout(左面的参数) << p(右面的参数)
ostream &operator<<(ostream &out, const Point &p) {
}

引用-代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/*************************************************************************
> File Name: 4.constructo.cpp
> Author: 秃头王
> Mail: 1658339000@qq.com
> Created Time: 2022年07月13日 星期三 13时12分06秒
************************************************************************/

#include <iostream>
using namespace std;

// 引用测试 引用相当于标签是贴在实参上面
void add_one(int &n) {
n += 1;
return ;
}

int main() {
int a = 1, b = 3;
cout << "normal param : " << a << " " << b << endl;
add_one(a);
add_one(b);

return 0;
}

构造函数与析构函数

构造/析构函数 使用方法
默认构造函数 People a;
People(string name); People(“ttw”);
People(consst People &a) 拷贝构造,与=不等价
~People

任何一个对象产生的时候都会调用构造函数,销毁的时候一定会调用析构函数

构造函数有参数:

  1. 一个参数: 通常叫转换构造
  2. 多个参数:

构造函数—代码实现

在类里面编译器会几类默认的行为:

  • 默认构造函数
  • 默认析构函数
  • 默认拷贝构造
  • 默认复制运算符
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
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
/*************************************************************************
> File Name: 4.constructo.cpp
> Author: 秃头王
> Mail: 1658339000@qq.com
> Created Time: 2022年07月13日 星期三 13时12分06秒
************************************************************************/

#include <iostream>
using namespace std;

class B1 {
public :
B1() = default; // 使用默认的无参构造(default/delete)
B1(const B1 &) { // 拷贝构造函数
cout << "B1 copy constructor" << endl;
}
};
class B2 {
public :
B2() = default;
B2(const B2 &) {
cout << "B2 copy constructor" << endl;
}
};

class B3 {
public :
B3() = default;
B3(const B3 &) {
cout << "B3 copy constructor" << endl;
}
};
class B {
public :
B1 b1;
B2 b2;
B3 b3;
};

class A {

public :

// 左面x是成员属性,而右面的x是参数x - 有参构造出现、默认无参构 造就会被删除掉了
A(int x) : x(x) { // x(x) 初始化列表
cout << this << " : Class A :" << x << endl;
}

// 默认也是有拷贝构造的
A(const A &a) {
cout << this << " : copy from " << &a << endl;
}

void operator=(const A &a) {
cout << this << " assign from " << &a << endl;
}

// 成员属性
int x;
};

class Data {

public :
/*
默认构造相当于这个无参
初始化列表_擅于使用初始化列表

初始化列表的顺序和声明的顺序有关系
如 Data() : __y(100), __x(__y + 1) {}
规定了每一个成员的属性的构造形式

会一次构造成员属性
__x
__y
a(3) // 显性的调用了

*/
Data() : __x(0), __y(0), a(3) {
/*
不完美的初始化
this->__x = 0;
this->__y = 0;
*/

// 为了确定走的这个构造参数
cout << "default constructor" << endl;
}

int x() { return __x ;}
int y() { return __y; }

~Data() {
cout << "destrcutor" << endl;
}

private :
int __x, __y;
A a;
};

int main() {
B b1;
B b2 = b1;

// 调用了默认构造函数
Data d;
cout << d.x() << " " << d.y() << endl;

// 有参构造_转换构造
A a(56);

// 调用了2次构造函数
A b = 45;
cout << "address b : " << &b << endl;

// 一次调用
b = 78;
return 0;
}

为什么拷贝构造要传引用?不加行不行?

image-20220713174652216

拷贝构造为什么不能去掉const?

int & 是不能绑定到匿名对象身上的

两点为什么要加:

  1. 加上const为了兼容匿名对象

  2. 逻辑上的常量限制

  3. 兼容const类型的拷贝

    const A a(45);

    A b = a;

拷贝执行流程

为什么多一个相当于 45 创建了一共匿名对象

image-20220713145426316

取消返回值优化

1
g++ -fno-elide-constructors test.cpp

析构函数

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
/*************************************************************************
> File Name: 6.constructo_order.cpp
> Author: 秃头王
> Mail: 1658339000@qq.com
> Created Time: 2022年07月13日 星期三 17时58分41秒
************************************************************************/

#include <iostream>
using namespace std;

class A {
public :
A() {
cout << this << " default cosntructor" << endl;
}

A(int n, int m) :
n(n), m(m),
arr(nullptr), size(nullptr),
offset(nullptr) {
cout << "A(int) constructor" << endl;
}

A(int *size, int *offset) : size(size), offset(offset) {
arr = new int[*size];
arr += *offset;
cout << "A(int *size) constructor" << endl;
}

~A() {
cout << this << " destructor" << endl;
if(arr == nullptr) return ;
arr -= *offset;
delete []arr;
arr = nullptr;
}
int *arr, *size, *offset ;
int n, m;
};

int main() {
A a(3, 4);
A b(&a.n, &a.m);
cout << "&a : " << &a << " &b :" << &b << endl;
return 0;
}

构造函数与析构函数顺序相反是特例么?还是一般情况?

一定是一般情况。

a对象定义在b前面,意味着b构造可能依赖于a对象,所以a一定先构造

image-20220713180345092

析构-代码

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
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
/*************************************************************************
> File Name: 6.constructo_order.cpp
> Author: 秃头王
> Mail: 1658339000@qq.com
> Created Time: 2022年07月13日 星期三 17时58分41秒
************************************************************************/

#include <iostream>
using namespace std;

#define BEGINS(x) namespace x {
#define ENDS(x) } // ned of namespace x


BEGINS(test1)

class A {
public :
A() {
cout << this << " default cosntructor" << endl;
}

A(int n, int m) :
n(n), m(m),
arr(nullptr), size(nullptr),
offset(nullptr) {
cout << "A(int) constructor" << endl;
}

A(int *size, int *offset) : size(size), offset(offset) {
arr = new int[*size];
arr += *offset;
cout << "A(int *size) constructor" << endl;
}

~A() {
cout << this << " destructor" << endl;
if(arr == nullptr) return ;
arr -= *offset;
delete []arr;
arr = nullptr;
}
int *arr, *size, *offset ;
int n, m;
};

void main() {
A a(3, 4);
A b(&a.n, &a.m);
cout << "&a : " << &a << " &b :" << &b << endl;
return ;
}

ENDS(test1);

BEGINS(test2)

// 4个属性类
#define ATTR_CLASS(x) class ATTR_##x {\
public :\
ATTR_##x() { \
cout << "ATTR_" #x " default constructor" << endl; \
} \
~ATTR_##x() { \
cout << "ATTR_" #x "destructor" << endl;\
}\
};


ATTR_CLASS(1);
ATTR_CLASS(2);
ATTR_CLASS(3);
ATTR_CLASS(4);

class A {

public :
A() {
cout << "Class A constructor" << endl;
}

~A() {
cout << "Class A destructor" << endl;
}

ATTR_1 a1;
ATTR_2 a2;
ATTR_3 a3;
ATTR_4 a4;
};


void main() {
A a;
return ;
}

ENDS(test2)

int main() {

test1::main();
cout << " ------------------------------------ " << endl;
test2::main();

return 0;
}

类属性与方法

如果把成员属性变成类属性?

在属性前面添加static关键字即可

  • 类属性

    全局只有一份, 牵一发而动全身, 一个类对象对类属性的修改,其他对象都会感知到

  • 成员属性

    每一个对象都有一份,随着对象的增加而增加

    成员属性,每个对象修改自己的成员属性别是不知道的因为每个对象都有一份

他们主要是性质上的差别,就导致上了功能上的差别

  • 类方法

​ 非对象行为:

​ 列如:毁灭全人类

不能访问this指针 (相当于非对象方法)

  • 成员方法

    对象行为:

    ​ 列如:人类

    ​ 说话、跑、

他们主要的差别是逻辑上的差别

很多功能我们可以设计成类方法或者是成员方法,主要是看相应方法的内部使不使用this指针

1
2
3
4
5
6
7
8
9
10
class People {
public :
// 类函数 -> 存储在 静态存储区
static void is_valid_height(double height);
private :
// 类属性 只有一份 牵一发而动全身
static int total_num;
// 成员属性
double __height;
};

const方法

const 放到方法后面

1
2
3
4
class People {
public :
string &name() const; // const方法,是不能修改任何成员属性的值的 --- 在特殊的功能下费改不可 可以把属性 编程 mutable int x;
};

const对象只能调用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
/*************************************************************************
> File Name: 7.const_method.cpp
> Author: 秃头王
> Mail: 1658339000@qq.com
> Created Time: 2022年07月13日 星期三 21时12分55秒
************************************************************************/

#include <iostream>
using namespace std;

#define BEGINS(x) namespace x {
#define ENDS(x) } // ned of namespace x


BEGINS(test1)

class A {
public :
A() { x = 200; }
// const方法,是不能修改任何成员属性的值的 --- 在特殊的功能下费改不可 可以把属性 编程 mutable
void say() const {
x = 123;
cout << x << endl;
}
mutable int x;
};

void main() {
const A a;
a.say();
return ;
}

ENDS(test1);

int main() {
test1::main();
return 0;
}

C++中的结构体与类

  • C++

​ struct 访问权限默认 public

​ class 访问权限默认 private

c/c++结构体的区别 C C++
成员函数 不能有 可以
静态成员 不能有 可以
访问控制 默认public,不能修改 public/private/protected
继承关系 不可以继承 可以从类或者其他结构体继承
初始化 不能直接初始化数据成员 可以

为什么C++还会保留struct关键字更好的兼容

用严格准确的逻辑, 将错误暴露在编译阶段

C++ 核心精髓:不仅要说话,还要说的漂亮话

浅拷贝与深拷贝

  • 浅拷贝:

    又称值拷贝,将原对象的值拷贝到目标对象中去,本质上来说源对象和目标对象公用一份实体,只是所引用的变量名不同,地址其实还是相同的。举个简单的例子,你的小名字叫嘻嘻,大名叫哈哈,当别人叫你嘻嘻或者哈哈的时候你都会应答,这个两个名字虽然不同,但是都指的是你

  • 深拷贝:

    拷贝的时候先开辟出和源对象大小一样的空间,然后将源对象里的内容拷贝到目标对象中去,这样两个指针就指向了不同的内存位置。并且里面的内容是一样的,这样不但达到了我们想要的目的,还不会出现问题,两个指针先后调用析构函数,分别释放自己所指向的位置,即为每次增加一个指针,便申请一块新的内存,并让这个指针指向新的内存,深拷贝情况下,不会出现重复释放同一块内存的错误

浅拷贝

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
51
52
53
54
55
/*************************************************************************
> File Name: 8.deep_copy.cpp
> Author: 秃头王
> Mail: 1658339000@qq.com
> Created Time: 2022年07月14日 星期四 09时55分27秒
************************************************************************/

#include <iostream>
using namespace std;

class Vector {
public :

Vector(int n = 100) : n(n), data(new int[n]) {}
// 返回数组第i位的引用
int &at(int ind) { return data[ind]; }

int &operator[](int ind) { return data[ind]; }

void output(int m = -1) {
if(m == -1) m = n;
cout << "arr " << this << " : ";
for(int i = 0; i < m; ++i) {
cout << data[i] << " ";
}
cout << endl;
return ;
}

private :
int n;
int *data;

};

int main() {
Vector arr;
for(int i = 0; i < 10; i++) {
// arr.at(i) = i; // 旧版
arr[i] = i; // 重载[]方括号运算符
}
arr.output(10);


Vector arr2(arr);
arr2.output(10);
// 修改arr2的值
arr2[3] = 777;

// 输出arr
arr.output(10);
arr2.output(10);

return 0;
}

image-20220714101154705

正常是思维:我想把arr拷贝给arr2, arr2是应该不影响arr1的

问题就处在默认的拷贝行为

image-20220714102322566

深拷贝

深拷贝使用 memcpy 是不对的,因为数据存储区也需要深拷贝时候

memcpy(data, a.data, sizeof(int) * n);

贴个有错误的代码

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
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
/*************************************************************************
> File Name: 8.deep_copy.cpp
> Author: 秃头王
> Mail: 1658339000@qq.com
> Created Time: 2022年07月14日 星期四 09时55分27秒
************************************************************************/

#include <iostream>
#include <string.h>
using namespace std;

#define BEGINS(x) namespace x {
#define ENDS(x) } // end of namespace x

BEGINS(ttw)

class A {
public :
int x, y;

};

ostream &operator<<(ostream &out, const A &a) {
out << "(" << a.x << "," << a.y << ")";
return out;
}

template<typename T>
class Vector {
public :

Vector(int n = 100) : n(n), data(new T[n]) {}
Vector(const Vector &a) : n(a.n) {
data = new T[n];
memcpy(data, a.data, sizeof(T) * n);
/*
for(int i = 0; i < n; i++) {
data[i] = a.data[i];
}
*/
return ;
}
// 返回数组第i位的引用
T &at(int ind) { return data[ind]; }
T &operator[](int ind) { return data[ind]; }

void output(int m = -1) {
if(m == -1) m = n;
cout << "arr " << this << " : ";
for(int i = 0; i < m; i++) {
cout << data[i] << " ";
}
cout << endl;
return ;
}

private :
int n;
T *data;

};

ENDS(ttw)

BEGINS(test1)

int main() {

ttw::Vector<int> arr1;
for(int i = 0; i < 10; i++) {
// arr.at(i) = i; // 旧版
arr1[i] = i; // 重载[]方括号运算符
}
arr1.output(10);


ttw::Vector<int> arr2(arr1);
arr2.output(10);
// 修改arr2的值
arr2[3] = 777;

// 输出arr
arr1.output(10);
arr2.output(10);

return 0;
}

ENDS(test1)

BEGINS(test2)

using namespace ttw;

int main() {

Vector<A> arr1;
for(int i = 0; i < 10; i++) {
arr1[i].x = i;
arr1[i].y = 2 * i;
}

arr1.output(10);
Vector<A> arr2(arr1);
arr2[3] = (A){4, 1000};

arr1.output(10);
arr2.output(10);

return 0;
}


ENDS(test2)

BEGINS(test3)

using namespace ttw;

int main() {

Vector<Vector<int>> arr1;
Vector<Vector<int>> arr2(arr1);
arr2[2][2] = 100;
for(int i = 0; i < 3; i++) {
arr1[i].output(3);
}

for(int i = 0; i < 3; i++) {
arr2[i].output(3);
}

return 0;
}


ENDS(test3)


int main() {
test1::main();
cout << " ------------------------------ " << endl;
test2::main();
cout << " ------------------------------ " << endl;
test3::main();
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
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
/*************************************************************************
> File Name: 8.deep_copy.cpp
> Author: 秃头王
> Mail: 1658339000@qq.com
> Created Time: 2022年07月14日 星期四 09时55分27秒
************************************************************************/

#include <iostream>
#include <string.h>
using namespace std;

#define BEGINS(x) namespace x {
#define ENDS(x) } // end of namespace x

BEGINS(ttw)

class A {
public :
int x, y;

};

ostream &operator<<(ostream &out, const A &a) {
out << "(" << a.x << "," << a.y << ")";
return out;
}

template<typename T>
class Vector {
public :

Vector(int n = 100) : n(n), data((T *)calloc(sizeof(T), n)) {}
Vector(const Vector &a) : n(a.n) {
// data = new T[n]; -> 会调用若干次构造函数
// memcpy(data, a.data, sizeof(T) * n); // 有问题不能用memcpy

data = (T *)malloc(sizeof(T) * n);

for(int i = 0; i < n; i++) {
// 原地构造 new(地址) -> 构造函数
// T 类型的拷贝构造
new (data + i) T(a.data[i]);
}

return ;
}
// 返回数组第i位的引用
T &at(int ind) { return data[ind]; }
T &operator[](int ind) { return data[ind]; }

void output(int m = -1) {
if(data == nullptr) return ;
if(m == -1) m = n;
cout << "arr " << this << " : ";
for(int i = 0; i < m; i++) {
cout << data[i] << " ";
}
cout << endl;
return ;
}

private :
int n;
T *data;

};

ENDS(ttw)

BEGINS(test1)

int main() {

ttw::Vector<int> arr1;
for(int i = 0; i < 10; i++) {
// arr.at(i) = i; // 旧版
arr1[i] = i; // 重载[]方括号运算符
}
arr1.output(10);


ttw::Vector<int> arr2(arr1);
arr2.output(10);
// 修改arr2的值
arr2[3] = 777;

// 输出arr
arr1.output(10);
arr2.output(10);

return 0;
}

ENDS(test1)

BEGINS(test2)

using namespace ttw;

int main() {

Vector<A> arr1;
for(int i = 0; i < 10; i++) {
arr1[i].x = i;
arr1[i].y = 2 * i;
}

arr1.output(10);
Vector<A> arr2(arr1);
arr2[3] = (A){4, 1000};

arr1.output(10);
arr2.output(10);

return 0;
}


ENDS(test2)

BEGINS(test3)

using namespace ttw;

int main() {

Vector<Vector<int>> arr1;
Vector<Vector<int>> arr2(arr1);
arr2[2][2] = 100;
for(int i = 0; i < 3; i++) {
arr1[i].output(3);
}

for(int i = 0; i < 3; i++) {
arr2[i].output(3);
}

return 0;
}


ENDS(test3)


int main() {
test1::main();
cout << " ------------------------------ " << endl;
test2::main();
cout << " ------------------------------ " << endl;
test3::main();
return 0;
}

返回值优化(RVO)

对象的初始化

什么是我们认为内存中完整的对象?

​ 那就是经历了构造过程的数据存储区

image-20220714172718696

拷贝构造

image-20220714172850098

返回值优化 (RVO)

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
/*************************************************************************
> File Name: 9.rvo.cpp
> Author: 秃头王
> Mail: 1658339000@qq.com
> Created Time: 2022年07月14日 星期四 17时30分39秒
************************************************************************/

#include <iostream>
using namespace std;

class A {

public :
A() {
cout << this << " : default constructor" << endl;
}
A(int x) {
cout << this << " : param constructor" << endl;
}
A(const A &a) {
cout << this << " : copy constructor" << endl;
}

};

A func() {
A temp(3); // 正常调用 有参构造
cout << "object temp : " << &temp << endl;
return temp;
}

int main() {
A a = func(); // 正常会被 调用拷贝构造
cout << "object a : " << &a << endl;
return 0;
}

正常情况下会被调用3次构造函数但是输出只调用了一次,而且用的还是有参构造

image-20220714173751408image-20220714174214497

Step 1:开辟a对象数据区

Step 2∶调用函数func

Step 3:开辟对象temp数据区

Step 4∶调用temp对象的构造函数

Step 5∶使用temp调用a的拷贝构造函数

Step 6∶销毁temp对象、

Step 7∶销毁a对象

取消返回值优化

image-20220714175505972

1
g++ -fno-elide-constructors 9.rvo.cpp

default/delete

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
/*************************************************************************
> File Name: 10.default_delete.cpp
> Author: 秃头王
> Mail: 1658339000@qq.com
> Created Time: 2022年07月14日 星期四 18时06分32秒
************************************************************************/

#include <iostream>
using namespace std;

class A {

public :
// 隐性的规则变成显性的规则
A() = default;
/*
A() = default;
A(){}
这两个是没有差别的

*/
A(const A &a) = delete; // 在当前类把这个方法/构造函数删除
/*
A(const A &a) = default;
A(const A &a) {} // 没有依次的拷贝成员属性的行为
两者完全不一样

*/

};

int main() {

return 0;
}

源码文件*.cpp

点我跳转:https://github.com/qzwl123/C-/tree/main/%E5%B0%81%E8%A3%85

评论