一. c++中的类(核心)
1.类的三大特征: 封装 继承 多态
- 封装:
- 继承 :
优点:
子类直接继承父类的接口函数, 就不需要重新开发接口
提高了代码的复用性 有利于软件版本的升级
- 多态
- 一个对象作用于不同的事物,所得到的结果(功能)不同
优点: 提高了代码的复用性
2.类的定义
语法:
C++ class 类名{ 成员列表; 方法.... } |
以上类的定义是最简单的定义方式,没有什么意义,默认成员和方法都是私有的,外界无法访问
需要修饰符来修饰成员和方法:
public 公有的 ------- 外界可以访问
protected 受保护的 ------- 外界不可以访问
private 私有的 ------- 外界不可以访问
二. C++中的构造函数
构造函数:
1 函数名和类名相同
2 函数没有返回值
3 函数不需要用户调用,创建对象的时候自动调用
构造函数作用: 用于定义对象时,初始化对象的数据成员
语法:
C++ class 类名{ public: 类名(){ //构造函数 } } |
案例:
C++ //定义类 class base{ public: // 构造函数必须写在public公共区 // 否则 无法创建对象 //构造函数----函数名和类名同名 //无参构造函数 base(){ cout << "创建对象,自动调用构造函数" << endl; } //带参构造函数 base(int a){ num = a; cout << num << endl; } private: int num; }; int main(int argc, char const *argv[]) { //定义对象调用构造函数 -- 无参 base a; //定义对象时传入参数 -- 带参 base b(10086); return 0; } |
注意:
- 构造函数必须写在公共区public里, 因为定义对象时会自动调用, 只有在公共区才可以被外界直接访问调用
- 假设用户没写任何的构造函数,系统会自动生成无参构造函数
练习: 设计一个学生类,公有成员姓名name(char *),保护成员money(double),私有成员密码pwd(int)
C++ 定义一个构造函数初始化这些数据成员 并写一个输出接口 #include <iostream> using namespace std; extern "C"{ #include <string.h> } /* 设计一个学生类, 公有成员姓名name(char *), 保护成员money(double), 私有成员密码pwd(int) 定义一个构造函数初始化这些数据成员 并写一个输出接口 */ class Stu{ public: char name[40]; //解决方式1: 函数重载 // Stu(){ // cout << "构造函数被调用" << endl; // } //正常步骤 // Stu(const char * n,double m, int p){ // cout << "构造函数被调用" << endl; // //初始化 // strncpy(name,n,strlen(n)+1); // money = m; // pwd = p; // } //解决方式2: 默认参数 //构造函数 Stu(const char * n="ZQ",double m=1.34, int p=123456){ cout << "构造函数被调用" << endl; //初始化 strncpy(name,n,strlen(n)+1); money = m; pwd = p; } void show(){ cout << "name:" << name << endl; cout << "money:" << money << endl; cout << "pwd:" << pwd << endl; } protected: double money; private: int pwd; }; int main(int argc, char const *argv[]) { //定义对象 -- 必须传参 Stu obj("ZQ",12345567.8,123456); //调用接口 obj.show(); //想法: 创建对象时 不传递参数 -- 简化 // 解决方式1: 函数重载 // Stu pp; //解决方式2: 默认参数 Stu pp; return 0; } |
总结:
构造函数支持函数重载
三. this关键字
this指针的作用:
- 每当用户创建一个类,这个类内部就会自动生成一个this指针, 指向当前类的首地址
C++ #include <iostream> using namespace std; // 给构造函数传参并输出 class base{ public: //构造函数 base(int a,int b,int c){ cout << "&this:" << this << endl; //this指针 指向是内部成员 // 用来和外部成员做区分 this->a = a; //this是指针 使用 -> this->b = b; this->c = c; } void show(){ cout << a << endl; cout << b << endl; cout << c << endl; } private: int a; int b; int c; }; int main(int argc, char const *argv[]) { /* code */ base obj(10,20,30); cout << "&obj:" << &obj << endl; obj.show(); return 0; } |
四.malloc和new 的区别
C++ class base{ public: base(int a){ cout << "调用构造函数" << endl; this->a = a; } void show(){ cout << a << endl; } private: int a; }; int main(int argc, char const *argv[]) { // 使用malloc 从堆空间分配内存 给base // 不会自动调用base的构造函数 // 导致类中的成员a 无法被初始化 // base * p = (base *) malloc(sizeof(base)); // p->show(); //因为p是指针 所以使用-> //=========================== // 可以使用new关键字申请内存 // new会自动调用构造函数 base *p = new base(10010); p->show(); return 0; } |
malloc 和 new 的区别:
- 使用方式。“new”是C++的语言特性,是一个操作符,使用时无需指定内存大小,编译器会根据类型信息自动计算;而“malloc”是C语言中的库函数,使用时需要显式地指出所需内存的尺寸。----new自动计算大小
- 返回值。“new”操作符在内存分配成功后返回的是对象类型的指针,无须进行类型转换;而“malloc”分配内存成功后返回的是void指针,需要通过强制类型转换将其转换为所需的类型。 ---- new自动类型转换
- 初始化。“new”不仅分配内存,还可以直接调用对象的构造函数进行初始化;而“malloc”仅分配内存,需要调用者自行初始化分配的内存。---- new 自动调用构造函数
五. 析构函数
析构函数特点:
1.函数名和类名相同,但需要在函数名之前加 ~ (波浪线)
2.析构函数没有返回值, 也没有参数
3.当对象被销毁时 系统自动调用析构函数(可以释放成员空间)
作用:
释放构造函数初始化的数据成员
注意:
- 析构函数不可以重载 重载的依据是参数,而析构函数没有
练习:
定义一个类,对数据成员进行初始化,数据成员是字符串类型 长度是1024
创建 3个对象 考虑内存泄漏的问题----必须调用析构函数
练习: 设计file类 , 在构造函数中打开文件,设计一个写入数据接口.在析构函数中关闭文件
C++ #include <iostream> using namespace std; extern "C"{ #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> #include <string.h> } //文件类 class file{ public: //构造函数 -- 打开文件 file(const char * filename = "test.txt"){ fd = open(filename, O_RDWR | O_CREAT,0777); if (fd < 0) { cout << "打开文件失败~" <<endl; }else{ cout << "打开文件成功~" << endl; } } //自定义接口-- 写入数据 int write(const char * str = "这是默认内容,你就懒吧~"){ // :: 使用最前面引入的头文件中声明的系统函数write int size = ::write(fd, str, strlen(str)); return size; } //析构函数 -- 关闭数据 ~file(){ close(fd); } private: int fd; //文件描述符 }; int main(int argc, char const *argv[]) { // //定义文件类的对象 // file ff; // //写入数据 // ff.write("今天就讲到这吧~"); file a("my.txt"); a.write(); return 0; } |
六.类的大小计算
类的大小计算和结构体一样
C++ struct Node{ int a; char c; double d; short s; }; //24字节 (64位) // 32位/64位 地址能被数据类型大小整除, //总数据大小要能被其中的最大数据类型大小整除类的大小 class base1{ private: char a; //2 protected: short b; //2 public: int c; //4 }; //大小 8 class base2{ public: char a; // 4 void fun(){ int m; float n; long long q; } protected: int b; //4 private: short c; //2 + 2 }; //大小 12 |
结论: 在设计类中的数据成员时, 应该将数据类型按照字节数从小到大的顺序去定义,这样定义可以节省内存,所占空间最小
注意:
类中的大小与成员属性定义顺序有关 -- 从小到大
类中的大小与成员函数 -- 无关!
注意:
C++ class base{ private: static int sta; //静态成员变量 } //类外进行初始化 int base::sta = 789; //静态成员变量 存储在 数据段中 不属于类内的空间中 |
注意: 类中的静态成员变量 ,无法在类内实现初始化,只能在类外进行初始化
七. (只用于)构造函数的参数列表初始化
C++ base(int a1, short b1, char c1):a(a1),b(b1),c(c1) { //注意: 以上的初始化方式并不是万能的 // 需满足一个条件: 数据类型必须支持 = 赋值 // 例如 数组 不支持=赋值 所以不能用以上方式 //初始化过程 // this->a = a1; // this->b = b1; // this->c = c1; } |
注意:
以上的初始化方式并不是万能的
需满足一个条件: 数据类型必须支持 = 赋值
例如 数组 不支持=赋值 所以不能用以上方式
练习: 要求定义一个构造函数 利用参数列表初始化的方式对数据成员进行初始化
C++ struct Node{ int a; char c; } class base{ private: int a; struct Node n; char str[100]; } |
目录
一. c++中的类(核心)
1.类的三大特征: 封装 继承 多态
2.类的定义
二. C++中的构造函数
三. this关键字
四.malloc和new 的区别
五. 析构函数
练习:
六.类的大小计算
七. (只用于)构造函数的参数列表初始化