-
应用场景:
- 高低级程序设计、嵌入式系统、数值计算方面、Adobe等
-
主要三方面内容:
- 封装:基础
- 继承:核心
- 多态:延展
-
c++中如何表达:
- int i;
- c: 定义一个变量 面向过程
- c++:实例化一个对象 面向对象
- c语言也可以实现c++思想
-
c++定义变量:
- 也是根据个人风格的;逐渐养成
- 比如:int num; char name;
- int i_num; char ch_name;等
-
读入、输出
- 读入流 cin >> val; 输出流 cout << val << endl;
-
错误流:cerr << "Error message : " << str << endl;
-
命名空间:namespace
- namespace NAME{
- 成员;
- }【没有分号】
- :: 域解析运算符
-
类:class
-
class className
{
public:
成员方法;(动态,函数或方法)
private:
数据成员;(静态,数据或数值)};
-
一对 { } 是成员列表边界符,与成员列表一起成为类体。类体后面必须用 ; 结束。
-
关于类内成员
-
每个类可以没有成员,也可以有多个成员。
-
类成员可以是数据或函数。
-
所有成员必须在类内部声明,一旦类定义完成后,就没有任何其他方式可以再增加或减少成员。
-
-
三种访问权限:
-
public:公有成员,类内类外均可访问
-
protected:保护成员,在没有发生继承关系时,同private
-
private:私有成员,只能类内访问,类外不可访问
-
如果在声明的时候不写访问控制属性,则类会默认它为private。
-
-
在面向对象程序设计中,一般将变量(数据成员)隐蔽起来,外部不能直接访问。把成员方法作为对外界的接口,通过成员方法访问数据,解决c程序访问数据的安全性问题
-
定义类中成员方法的方式
-
在类内部定义:程序在要调用它的时候把它当作是一个内联函数,内联函数的好处是调用速度更快,但是会占用额外的内存空间,每调用一次都相当于定义一次,inline关键退化为建议型关键字,由编译器来决定是否将某函数作为内联
-
在类外部定义:可以是递归的函数
-
-
类的声明是否占用存储空间?
-
不占用存储空间
-
仅当实例化对象时,对象占用相对应的大小空间
-
-
访问对象中的成员有三种方法:
通过对象名和对象成员引用运算符 (.)
通过指向对象的指针和指针成员引用运算符 (->)
通过对象的引用变量和对象成员引用运算符 (.)
-
最后一种访问方式:Test test, &r = test; r.Sum();
-
-
练习01:
-
抽象一个长方体类,方法:1获取长宽高,2计算体积
-
-
动态指针对象
-
Test *p = new Test; delete p; p = NULL;
-
Test *p = new Test[3]; delete [ ] p; p = NULL;
-
-
引用:
-
理解为某块空间/某个变量的别名,实际常常可以达到0开销
-
引用必须在定义时就指定引用的对象,且不可更改
-
C中定义变量均可用:TYPE NAME = VALUE; 如:int a[3]; -> int[3] a;
-
char类型的引用,本质上相当于 sizeof(char)
-
-
函数传参:(参数带默认值)
-
若用户进行传参,则以传参为准
-
若用户未进行相应参数传递,则使用默认值
-
从带默认值的形参往后的所有形参必须全部带默认值
-
C++中对于特定的某个函数,设置默认形参这个动作只能有一次
-
-
类中的特殊函数:构造函数,拷贝构造函数,析构函数
-
构造函数:作用初始化某一个对象
-
在实例化一个对象时被自动隐式调用,且无须显示调用
-
我们若没有自行实现,则系统会为我们生成一个默认的构造函数,若我们自行实现了一个或多个构造函数,则系统默认的不会再生成
-
外形特点:构造函数名与类同名,可传参,可不传,无返回值,构造函数可存在一个或多个
-
构造函数可重载,参数可以有默认值
-
#include <iostream> using namespace std; class Test { public: Test() {} Test(int x,int y):a(x),b(y) {} void Sum(); private: int a,b; }; void Test::Sum() { cout<<a+b; } class AnotherTest { public: AnotherTest(int i,int j):test(i,j) {test.Sum();} private: Test test; }; int main() { AnotherTest test(3,4); return 0; } Test(int x = 0,int y):a(x),b(y) {} //这样是错误的。
-
-
析构函数:作用是清理和释放
- 在一个对象即将消亡的时候被自动的隐式调用,通常调用顺序与构造逆序
- 我们若没有自行实现,则系统会为我们生成一个默认的析构函数,一旦我们自行实现,则不会再有系统默认的生成
- 外形特点:析构函数与类同名,前面加个~,析构函数只能有一个,无传参,无返回值
-
析构函数何时被调用?
- 生命周期结束 (在栈区创建的对象,生命周期结束时,会自动执行析构函数)
- delete (用new创建的对象,函数返回时,不会自动执行析构函数,需要手动delete才能执行析构函数。注意:不主动delete,容易造成内存泄漏)
- 包含关系 (当对象1的private里有对象2时,当对象1析构后,对象2会自动析构)
- 继承关系 (栈区子对象生命周期结束时,会先执行自身析构函数,再执行对象的析构函数)
- new调用构造函数、delete调用析构函数
- 引荐实现代码
https://blog.csdn.net/Dontla/article/details/126659030?app_version=6.2.1&code=app_1562916241&csdn_share_tail=%7B%22type%22%3A%22blog%22%2C%22rType%22%3A%22article%22%2C%22rId%22%3A%22126659030%22%2C%22source%22%3A%22weixin_51984896%22%7D&uLinkId=usr1mkqgl919blen&utm_source=app
-
拷贝构造函数:作用初始化某一个对象
- 在实例化一个对象时,用相同类型的已有对象给新对象初始化,这时调用拷贝构造函数而不是构造函数
- 外形特点:函数名与类名相同,传参为一个常引用,无返回值
- 若我们没有自行实现,则系统会生成一个默认的,如自行实现,默认的不再生成
- 定义一个类至少有几个方法?默认构造方法、默认的拷贝构造方法、析构方法
-
-
练习02:
-
构建一个Person类,私有数据成员为姓名(char*)+年龄(int)+ 常量性别,公有方法:构造,析构,拷贝构造,赋值,打印,改名。
-
-
浅拷贝与深拷贝:通俗点说
-
浅拷贝:默认生成的拷贝构造函数就是此种方式,相应的成员之间直接完成了赋值操作,包括动态分配的空间(即指针成员)
-
深拷贝:拷贝类中的成员时,将成员所对应的动态存储空间也另外开辟一份,各自使用各自的
-
什么时候需要深拷贝? 类中有指针成员变量的时候
-
类中有指针,并动态开辟,一定要显示定义拷贝构造方法
-
-
this指针:
-
在类中的特殊函数中,this指针被当作默认的隐藏的传参,this指针指向当前调用该方法的对象
-
-
初始化列表:作用初始化类中的成员
-
构造方法函数声明后的位置叫做初始化列表位置
-
适用情况:类中有引用成员变量,const成员,对象成员
-
-
练习03:
-
类的静态数据成员及静态成员方法
-
静态数据成员:通常是用于统计或计数,它不从属于任何一个对象,存储空间只有一份,只是类中的一个成员。在类中仅作声明,类外初始化,通常需要初始化为0,并且在类内可直接访问,类外,通常<类名>::<静态成员名>,也可只是静态成员名。
-
静态成员变量是在程序编译时分配空间,而在程序结束时释放空间。
-
不能用参数初始化表,对静态成员变量进行初始化
-
既可以通过类名来对静态成员变量进行引用,也可以通过对象名来对静态成员变量进行引用。[私有除外]
-
静态成员方法:访问类中的静态数据成员,没有this指针参与,不能访问类中的普通数据成员。<类名>::<静态成员方法名()>
-
不能通过类名来调用类的非静态成员方法
-
类的对象可以使用静态成员方法和非静态成员方法。
-
静态成员方法中不能引用非静态成员。
-
类的非静态成员方法可以调用静态成员方法,但反之不能。
-
类的静态成员变量必须先初始化再使用。
-
静态数据成员是所有类对象所共有的
-
https://blog.csdn.net/Chroniccandy/article/details/108621102
-
-
友元函数、友元类
-
你要使用我私有的东西,我就需要提前声明你是我的朋友,打破c++封装概念
-
友元函数是单向的、友元函数不可继承、友元函数没有this指针
-
https://zhuanlan.zhihu.com/p/158805900#:~:text=%E5%8F%8B%E5%85%83%E5%8F%AF%E4%BB%A5%E6%98%AF%E4%B8%80%E4%B8%AA%E5%87%BD%E6%95%B0%EF%BC%8C%E4%B9%9F%E5%8F%AF%E4%BB%A5%E6%98%AF%E4%B8%80%E4%B8%AA%E7%B1%BB%EF%BC%8C%E8%AF%A5%E7%B1%BB%E8%A2%AB%E7%A7%B0%E4%B8%BA%E5%8F%8B%E5%85%83%E7%B1%BB%E3%80%82,%E5%8F%8B%E5%85%83%E7%B1%BB%E7%9A%84%E6%89%80%E6%9C%89%E6%88%90%E5%91%98%E5%87%BD%E6%95%B0%E9%83%BD%E6%98%AF%E5%8F%A6%E4%B8%80%E4%B8%AA%E7%B1%BB%E7%9A%84%E5%8F%8B%E5%85%83%E5%87%BD%E6%95%B0%EF%BC%8C%E9%83%BD%E5%8F%AF%E4%BB%A5%E8%AE%BF%E9%97%AE%E5%8F%A6%E4%B8%80%E4%B8%AA%E7%B1%BB%E4%B8%AD%E7%9A%84%E4%BF%9D%E6%8A%A4%E6%88%90%E5%91%98%E5%92%8C%E7%A7%81%E6%9C%89%E6%88%90%E5%91%98%E3%80%82
-
-
组合
-
一个类包含另一个类对象
-
https://blog.csdn.net/qq_45481606/article/details/119920168
-
-
继承
- 作用:减少代码的重复,提高程序段的可读性,后续功能方便添加
- 继承方式:public protected private
- 基类 & 派生类 | 父类 & 子类
-
访问控制和继承
-
一个派生类继承了所有的基类方法,但下列情况除外:
-
基类的构造函数、拷贝构造函数、析构函数
-
基类的重载运算符
- 基类的友元函数
-
-
多继承
-
class <派生类名>:<继承方式1><基类名1>,<继承方式2><基类名2>,…
{ <派生类类体> };
-
-
多继承(环状继承),A->D, B->D, C->(A,B)
-
这个继承会使D创建两个对象,要解决上面问题就要用虚拟继承格式
-
格式:class 类名: virtual 继承方式 父类名
-
虚继承--(在创建对象的时候会创建一个虚表)在创建父类对象的时候
- 虚继承-- 避免多继承的命名冲突
-
-
多层继承、菱形继承等
-
练习04:
-
使用c++实现链表的增删改查
-
实现栈、队列
-
-
多继承的构造函数调用顺序【继承皆如此】
-
比如说快递箱子,内部的物品是基类,外层箱子是派生类。所以构造顺序为基类、派生类。 当基类(派生类)构造函数有参数时,派生类需要在写构造函数后初始化基类(派生类),默认构造函数无参;当对象消亡时,先拆箱子,之后再取走物品。调用析构的顺序为派生类、基类(派生类)。
-
当然基类中的【数据成员、成员方法】,派生类是完全继承的,私有的可继承不可见,基类中的构造函数、拷贝构造函数、析构函数,重载运算符、基类的友元函数是派生类不可继承的。
-
在实际生产环境中,尽量避免多继承,尤其是菱形继承、环状继承
-
-
遮蔽(隐藏)
-
在继承关系中,派生类中有与基类同名的方法或数据,此时调用该名字默认使用派生类自己的内容,称为遮蔽了基类的内容,常用于将基类的同名函数重新实现。
-
-
覆盖(重写)
- 在继承关系中,基类为virtual修饰,派生类与基类中成员方法同名,参数相同
-
重载
-
形式:函数名一样,参数不同(参数个数不同,参数类型不同,参数顺序不同),这就称为函数重载。
-
函数返回值不同不能作为判断重载的条件。
-
-
概念混淆
- 重载
- 同一个函数的不同表现形式
- 条件
- 同一个类
- 函数名相同
- 参数列表不同(个数、类型、顺序)
- 返回值不做要求
- virtual关键字可有可无
- 覆盖(重写)
- 继承关系中,派生类对基类同名函数有不同的表现形式
- 条件
- 有继承关系的类
- 函数原型相同(函数名,参数列表)
- 返回值没有要求
- 基类成员函数必须声明为虚函数(virtual)
- 遮蔽(隐藏)
- 继承关系中,派生类遮蔽掉基类的成员函数(名字隐藏了基类的方法)
- 对基类中成员函数是否有virtual不做要求
- 条件
- 继承关系
- 两种情况
- 派生类成员函数与基类成员函数函数原型一致,基类的成员函数没有virtual修饰,派生类中隐藏了基类的函数(区别覆盖)
- 派生类成员函数与基类成员函数函数原型不同,基类的成员函数可以有virtual,派生类中也隐藏了基类的函数(区别重载)
- 重载
-
类型兼容原则
- 一个公有派生类的对象在使用上可以被当作基类的对象使用,反之禁止
- c++允许对象间复制的
- 向上转型:(如上述类型兼容原则)编译器会生成对应的函数
- 向下转型:编译器生成的默认的函数不一定能够满足
-
练习05:
-
用person类(name,age,sex)派生出student类(int no,float score),实例化出n个学生,求这些学生的成绩平均值。
-
抽象出一个类point(int x,int y),实例化出两个点,求两个点间的距离
-
-
练习中遇到的问题:
- 在实现导入其他文件时,先测试下是否能运行
- 静态数据成员是独立的空间,对象的空间释放与静态数据成员空间无关
- 静态数据成员一定要初始化后使用,静态成员变量只能被静态成员函数调用
- 设置成员方法中的默认值时,最好方法声明时设置,并且这个动作只需一次
- 常量,引用等初始化列表时,最好方法定义时设置
- 继承中,基类构造函数需要参数时,派生类必须要进行初始化传参设置,写在定义时
- 构造函数无返回值,可重载,函数名与类名一致,有参
- 析构函数无返回值,只有一个,函数名与类名一致,类名前加~,无参
- 设置友元类时无需导入友元类的头文件
- 在写友元函数时应该先写要定义的类,后写我要声明调用的类,与友元类相反
- 友元函数的定义,需要在函数外部定义,内部只做声明
-
补充
- string是字符串类
- string类是一个模板类,位于名字空间std中
- 可以用 ==、>、<、>=、<=、和!=比较字符串,可以用 + 或者 += 操作符连接两个字符串,并且可以用 [ ] 获取特定的字符。
- string特性描述
-
int size()const; //返回当前字符串的大小 str.size() int length()const; //返回当前字符串的长度 str.length() 等
- string是字符串类
C++ 开发【深入浅出】笔记01
于 2023-10-31 17:07:17 首次发布