目录
一、声明和定义全放在类体中。需注意:成员函数如果在类体中定义,编译器可能会将其当成内联函数处理。
二、类声明放在.h文件中,成员函数定义放在.cpp文件中,注意:成员函数名前需要加类名::
解答(注意:参数中的this是编译器加的,自己编码时不能加):
一、面向过程与面向对象的初步认识
C语言是面向过程的,关注的是过程。
拿洗衣服来举列子:
面向过程是:
首先,你得有个盆子,然后接水,放衣服进去,接着放洗衣粉泡一会,用手搓或者刷子刷,换一下水过一遍,再搓一遍,再过一遍水,然后拧干,挂好衣架上晾干。
C++是面向对象的,关注的是对象,将一件事拆分成不同的对象,靠对象之间的交互完成。
接着拿洗衣服来举列子:
这次我们用洗衣机来洗
那么对象有:人,洗衣机,洗衣粉,衣服
整个过程为:人把衣服放进洗衣机里,倒入洗衣粉,按下启动键,洗衣机就会洗好衣服并且同时会把衣服甩干。
整个过程主要是:在人、洗衣机、洗衣粉、衣服四个对象之间完成的,人不需要关注洗衣机具体是怎么洗衣服的,是怎么甩干的。
简单总结一下:
面向过程你不仅需要关注对象,更重要的是关注你需要完成的事情的对象之间的整个实现逻辑过程是如何一步一步的实现的。
面向对象的话,你只需要关注实现这件事情有哪些对象,它们之间的关系是啥,其具体的实现过程你不需要关注。就相对于面向过程简单了很多。
二、类的引入
C语言结构体中只能定义变量,在C++中,结构体内不仅可以定义变量,也可以定义函数。
例如:
C的结构体定义:
C++中的结构体定义:
提示:上面结构体定义中,C++更喜欢用 class来代替 struct
三、类的定义
class classname
{
//类体,由成员函数和成员变量组成
};//注意,后面结尾这里有分号
类定义的两种方式:
一、声明和定义全放在类体中。需注意:成员函数如果在类体中定义,编译器可能会将其当成内联函数处理。
//类
struct Stack {//这里可以看到C++中将变量和函数全都定义在了结构体中
void Init(int n) {//栈的初始化
a = (int*)malloc(sizeof(int) * n);
if (nullptr == a) {
perror("malloc申请空间失败");
return;
}
capacity = n;
size = 0;
}
int* a;//用来初始化的临时变量
int size;//栈的大小,也即是栈中的数据个数
int capacity;//栈容量
};
二、类声明放在.h文件中,成员函数定义放在.cpp文件中,注意:成员函数名前需要加类名::
.h文件
#pragma once
#include<iostream>
#include <string.h>
//类
//成员函数和成员变量分别定义
struct Stack {
//成员函数
void Init(int n = 4);
void Push(int x);
//成员变量
int* a;//用来初始化的临时变量
int size;//栈的大小,也即是栈中的数据个数
int capacity;//栈容量
};
.cpp文件
#define _CRT_SECURE_NO_WARNINGS
#include "Stack.h"
void Stack :: Init(int n = 4) {//栈的初始化
a = (int*)malloc(sizeof(int) * n);
if (nullptr == a) {
perror("malloc申请空间失败");
return;
}
}
void Stack::Push(int x) {
}
四、类的访问限定符及封装
4.1 访问限定符
C++实现封装的方式:用类将对象的属性与方法结合在一块,让对象更加完善,通过访问权限选择性的将其接口提供给外部的用户使用。
访问限定符主要有以下三个:
public(公有)protected(保护)
private(私有)
【访问限定符说明】
1.public修饰的成员在类外可以直接被访问
2. protected和private修饰的成员在类外不能直接被访问(此处protected和private是类似的)
3.访问权限作用域从该访问限定符出现的位置开始直到下一个访问限定符出现时为止
4.如果后面没有访问限定符,作用域就到 } 即类结束。
5.class的默认访问权限为private,strugt为public(因为strugt要兼容C)
注意:访问限定符只在编译时有用,当数据映射到内存后,没有任何访问限定符上的区别
额外补充:
C++中struct和class的区别是什么?
struct定义的类默认访问权限是public,class定义的类默认访问权限是private。
具体例子:
如下,我们将结构体改成用class来修饰:
则在main函数中:
4.2 封装
面向对象有三大特征:封装、继承、多态
封装定义:将数据和操作数据的方法进行有机结合,隐藏对象的属性和实现细节,仅对外公开接口来和对象进行交互。
在C++中可以通过合理利用访问权限(private,public,protect)来隐藏代码实现的细节,控制那些方法可以在类外被引用等。
如下:
这里,我定义了一个包的类,因为这是我的私人物品,不想让别人拿我的东西。所以我将成员变量的访问权限设置为了private。这样,类外就无法访问和调用了。
五、类的实例化
定义:
用类 类型创建对象的过程,称为类的实例化
类的特征:
1.类是对对象进行描述的,是一个模型一样的东西,限定了类有那些成员,定义一个类并没有分配实际的内存空间来存储它。
2.一个类可以实例化多个对象。实例化出来的对象,占实际的内存空间,存储类成员变量
通俗例子:
把定义的类看成现实生活中的设计一个水塔的图纸
在你没有实际动手去造这玩意的时候,它就只是一张纸,一个概念,没有占用实际的空间,存不了水。
实例化,则指的是你根据图纸在现实中造出来了这个水塔,才可以用来存储水了。
并且,有了一张图纸就可以造很多个水塔了,就是说一个类可以实例化多个对象
具体实例:
六、计算类对象的大小:
小问题:
这里我们看到,Bag类的大小只有8字节,那就是只计算了两个成员变量的大小。那么成员函数呢?它难道就不占用内存空间的么?真的么!
答案是错误的。
成员函数肯定是得占用内存空间的。
因为每个实例化出来的对象其成员变量是不一样的,需要独立存储
但每个对象调用的成员函数是一样的,实现的是同样的功能
那么思考下,如果成员函数和成员变量一样,每实例化一次,就又定义一下一模一样的函数,这样是不是很浪费空间了。
所以,将成员函数放到一个共享的公共区域(代码段),实例化的对象需要时,去调用就行了,节省空间。
小结:
1.一个类的大小,实际就是该类中的 "成员变量"之和,当然要注意内存对齐。
2.注意空类的大小,空类比较特殊,编译器给了空类一个字节用来标识这个类对象。
补充:
内存对齐规则:
1.第一个成员在与结构体偏移量为0的地址处。
2.其他成员变量要对齐到某个数字(对齐数)的整数倍的地址处。
注意:对齐数=编译器默认的一个对齐数与该成员大小的较小值。
VS中默认的对齐数为8
3.结构体总大小为:最大对齐数(所有变量类型最大者与默认对齐参数取最小)的整数倍。
4.如果嵌套了结构体的情况,嵌套的结构体对齐到自己的最大对齐数的整数倍处,结构体的整体大小就是所有最大对齐数(含嵌套结构体的对齐数)的整数倍。
七、this指针
场景导入:
我们有了水塔设计图,接下来需要建造两座水塔。(其实这里是两个书包的对象实例化)
代码如下:
由上一部分中的计算类对象的大小的小问题中,我们知道了类实例化的成员函数是统一放到一个共享的公共区域(代码段)当中的。
那么在这里,接着问一个问题。
问题:
我们都知道实例化的对象是共用一个函数的。比如上面的Init函数。
那么我想问在其调用的时候,它又是如何知道实例化的b1调用Init函数时,它的参数就是(3,4)。而实例化的b2调用Init函数时,它的参数就是(5,6)呢?它们不是调用的都是同一个函数的么?它是怎么区分这个参数的区别的呢?
解答(注意:参数中的this是编译器加的,自己编码时不能加):
由于编码器在这里会帮我们添加好一个this来指向相应的函数,所以我们在实例化调用时不会产生分歧。(this存储在栈中,因为其是一个隐含的形参)
额外补充:
我们在Bag类中新增了一个Add函数
那么以下main函数中,其能不能顺利通过呢?
运行结果:
由以上我们知道,Init函数无法运行通过。但Add函数却通过了,为什么呢?
其实,不一定有 " -> "这个符号就一定产生了解引用。如果查看汇编代码,发现这一段只是
call了一下,也即是说直接调用了这个函数,并未对其解引用。
八、C++类和C的struct对比(本篇小结)
类:
1.数据和方法都可以封装到类中
2.控制访问方式。愿意给你访问设置属性公有,不愿意给你访问设置私有。
struct:
1.数据和方法是分离的,结构体中只能定义变量,不能定义函数
2.数据访问控制是自由的,其struct定义的变量的属性都是公有的,任何人去访问都是不受限制的。