1.C语言到C++速成

不同点?

C是面向过程的过程。C++是面向对象的高级语言。C更偏向于写一些底层的操作:比如 嵌入式,驱动开发

C++向下兼容C

C++比C多了函数库

HOW学C++?

当然肯定是把C学好咯。这里简单介绍一下C++就好了

首先我们看一个C++的hello的入门代码

1
2
3
4
5
6
7
8
9
#include <iostream>
using namespace std;

int main() {

cout << "Hello World" << endl;

return 0;
}

在来看一下一个比较复杂的C语言的代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#pragma warning(disable:4996)
#include <stdio.h>
int main()
{
long long m = 0, fm = 1, sum = 1;
int n, i;
while (scanf("%d", &n), n != -1) {
m = 0, fm = 1, sum = 1;
for (i = 0; i < n; i++)
{
fm = m + 1;
m = sum;
sum = m + fm;
}
printf("%lld %lld\n", m, sum);
}
return 0;
}

和这个C语言等价的C++代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include <iostream>
using namespace std;
int main()
{
long long m = 0, fm = 1, sum = 1;
int n, i;
while (cin >> n, n != -1) {
m = 0, fm = 1, sum = 1;
for (i = 0; i < n; i++)
{
fm = m + 1;
m = sum;
sum = m + fm;
}
//printf("%lld %lld\n", m, sum);
cout << m << " " << sum << endl;
}
return 0;
}

#include :和#include <stdio.h>差不多

using namespace std;不理解过这句话,反正就是要。

定义变量的方式和C一样。

cin全名叫 标准输⼊流和C语言的scanf差不多,对比理解。这个更方便,因为不需要变量的类型直接cout<<(变量名字)

cout全名叫 标准输出流和C语言中的printf差不多。也是直接cout>>(变量名)就可了

endl 和 ‘\n’ 换行符 差不多。

其他的for if else 和C 一样

2.C++封装和继承

封装

定义:封装就是把抽象得到的数据和行为相结构,形成一个有机的整体,也就是把数据操作数据的源代码进行有机的结合,形成类。其中数据和函数都是类的成员,目的在于对于对象的使用者和设计着分开,可以提高软件的可维护性和可修改性

特性:1.结核性,就是把属性和方法结合。2.信息隐藏性,利用接口机制隐藏内部实现的细节,只留下对外的接口。3.实现代码的重复利用

继承

定义:继承是新类从已有的类那里得到已有的特性。类的派生指的是从已有类产生新类的过程。原有的类成为基类或父类,产生的新类为派生类或者子类,子类继承父类后,可以创建子类对象来调用基类函数,变量等

单一继承:继承一个父类

多重继承:继承多个父类,类于类之间需要用逗号隔开,类名之前要又继承权限,假设两个或两个基类都有某个变量或者函数,在子类中调用的时候需要加上类的名字加以限定修饰 c.a::i = 1;

菱形继承:也就是继承的样子成为了一个菱形。B 和 C同时分别单一继承 A , D 多重继承B和C。着就是菱形继承。

但是这样D里面就有2份A 如何解决呢?就出现了虚继承。这里就不展开讲述了。现在只是稍微的知道点C++而不是深入的学习C++

继承权限:继承方式规定了如何访问继承的基类的成员。继承方式指定了子类成员以及类外对于从基类继承的成员的访问权限

继承权限:子类继承基类除构造和析构函数以外的所有成员

继承可以扩展已存在的代码,目的也是为了代码重用

继承分为接口继承和实现继承

普通成员函数的接口总是会被继承:子类继承一份接口和一份强制实现

普通虚函数被子类重写:子类继承一份接口和一份缺省实现【默认..】

纯虚函数只能被子类继承接口:子类继承一份接口,没有继承实现

image-20230508223430823

例子:这个例子是编译不成功的,只是为了更好的说明继承权限的作用罢了

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
class Animal //⽗类
{
public:
void eat()
{
cout << "animal eat" << endl;
}
protected:
void sleep()
{
cout << "animal sleep" << endl;
}
private:
void breathe()
{
cout << "animal breathe" << endl;
}
};
class Fish : public Animal //⼦类
{
public:
void test()
{
eat(); //此时eat()的访问权限为public,在类内部能够访问
sleep(); //此时sleep()的访问权限为protected,在类内部能够访问
breathe(); //此时breathe()的访问权限为no access,在类内部不能够访问
}
};
int main(void)
{
Fish f;
f.eat(); //此时eat()的访问权限为public,在类外部能够访问
f.sleep(); //此时sleep()的访问权限为protected,在类外部不能够访问
f.breathe(); //此时breathe()的访问权限为no access,在类外部不能够访问
}

ok

再来看一个例子

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
#include <iostream>
using namespace std;
class Adder {
public:
// 构造函数
Adder(int i = 0)
{
total = i;
}
// 对外的接⼝
void addNum(int number)
{
total += number;
}
// 对外的接⼝
int getTotal()
{
return total;
};
private:
// 对外隐藏的数据
int total;
};
int main()
{
Adder a;

a.addNum(10);
a.addNum(20);
a.addNum(30);
cout << "Total " << a.getTotal() << endl;
return 0;
}
  • 构造函数就是对象在被生成的时候会自动执行的代码,且名字需要和类的名字一样。

直接类名字()

  • 析构函数就是对象被删除的时候会自动执行的代码,也需要和类的名字一样。

直接 ~类名字()

对外隐藏的private 只有通过接口来实现对其的使用

1
2
3
4
5
6
7
8
9
10
public:
void fun2()
{
fun1();
}
private:
void fun1()
{
cout<<"fun1"<<endl;
}

调用fun2就相当于去执行了fun1

这就是我自己的片面的理解,我也没学过…..

3.生命周期和作用域

作用域和生存周期:

C++变量有2个非常重要的属性:作用域和生命周期。从两个不同的维度描述了一个变量–时间和空间。

作用域:是一个变量可以被引用的范围。

生命周期:是一个变量可以被引用的时间段。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <iostream>
using namespace std;
void blocky()
{
{
int i = 1;
{
cout << "i = " << i << endl;
{
int q = 1;
cout << "q = " << q << endl;
}
cout << "q = " << q << endl;//这个q访问不到
}
cout << "i = " << i << endl;
}
cout << "i = " << i << endl;//这里的i访问不到
}
int main()
{
blocky();
return 0;

可以发现用{}括起来的是一个完整的代码区域,由于编译器的顺序编译,q在里面定义的,所以在外面就访问不到,显然就是超出了q的作用域

image-20230508231153378

一个程序系统会分配内存块为4个区域:

  1. 代码区:存放程序的代码,也就是程序的各个函数代码块
  2. 全局数据区:存放程序的全局数据和静态数据
  3. 堆区:存放数据的动态数据
  4. 栈区:存放程序的局部数据,也就是各个函数中的数据

初始化的数据放在.data,未初始化的数据放在bss

1
2
int x;//bss段
int y=1;//.data段

局部变量也就是内部变量。局部变量是在函数内定义说明。作用域只在函数里面呢,离开了函数调用就错啦

全局变量也就是外部变量,在函数的外部定义的变量。不属于任何一个函数,而是属于一个源程序文件。作用域是整个源程序

全局变量

  • 作用域:全局作用域(全局变量只需要在一个源文件中定义,就可以作用域所有的源文件)
  • 生命周期:程序只要在运行就一直有
  • 引用方式:在其他文件中使用必须使用extern关键字去生命要应用的全局变量
  • 内存分布:全局数据区
  • 注意:如果两个文件中都定义了相同名字的全局变量,连接就会错误

静态全局变量

  • 作用域:只在被定义的文件中可见
  • 生命周期:程序运行期一直存放
  • 内存分布:全局数据区
  • 定义的方式:static关键字和const关键字

这里static 和 const 是不一样的,自行百度。这里只是大概知道。以后会出C和C++详细的学习

局部变量

  • 作用域:只在局部作用域中可以见
  • 生命周期:程序运行出局的时候作用域就无啦
  • 内存分布:栈区

静态局部变量

  • 作用域:只能在局部的作用域可以见
  • 生命周期:程序运行就一直在
  • 内存分布:全局数据区
  • 定义方法:在局部作用域中用static定义

4.This指针

在C++中,每一个对象都能通过this指针来访问自己的地址this指针式所有成员函数的隐含的参数。因此,在成员函数内部可以用它来指向调用的对象。

this指针的使用

一种情况是:在类的非静态成员函数中返回类对象本身的时候,直接用 return *this;

另外一种情况是:当参数和成员变量相同的时候。this->n=n【不能写n=n】

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <iostream>
using namespace std;
class Point
{
int x, y;
public:
Point(int a, int b) { x = a; y = b; }
void MovePoint(int a, int b) { x += a; y += b; }
void print() { cout << "x=" << x << "y=" << y << endl; }
};
void main()
{
Point point1(10, 10); //给point1赋值
point1.MovePoint(2, 2); //当对象point1调⽤MovePoint(2,2)函数时,即将point1对象的地址传递给了this指针。
point1.print();
}

image-20230508234452691

先对point1进行赋值后,然后调用Point中的MovePoint函数对point1的值进行了改变。

MovePoint函数的原型是void MovePoint(Point *this, int a, int b);第一个参数是指向该类对象的一个指针,我们在定义成员函数时没看见这个参数是因为这个参数在类中是隐藏的。这样point1的地址传递给了this,所以MovePoin函数中便显式的写成:

1
void MovePoint(int a, int b) { this->x +=a; this-> y+= b;}

所以该函数过程可以写成

1
point1.x+=a;point1.y+=b;

对于一个类的实例来说,你可以看到它的成员函数,成员变量。但是看不到它的实例本身。

this式一个指针,它时刻指向你这个实例本身。

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
#include <iostream>
using namespace std;
class A
{
public:
int get() { return i; }
void set(int x) {
this->i = x;
cout << "this指针保存的内存地址为:" <<this << endl;
}
private:
int i;
};
int main()
{
A a;
a.set(9);
cout << "对象a所在的内存地址为:" << &a << endl;
cout << "对象a所保存的值为:" << a.get() << endl;
cout << endl;
A b;
b.set(999);
cout << "对象b所在的内存地址为:" << &b << endl;
cout << "对象b所保存的值为:" << b.get() << endl;
return 0;
}

image-20230508235128026

OK发现一个很牛皮的东西:this指针指向当前对象!!!!

对象a的内存地址和对象a的this指针的地址相同。都是010FFCF0

当运行到对象b的时候,它的内存地址和它所对应的this指针指向的内存地址式一样的,说明this指针记录变量的式当前对象的内存地址。

this指针指向当前对象