c++结课小结
期末周终于结束了哭唧唧~明天考完C++就正式进入暑假啦!
对象所占据的空间只是用于存放
数据成员
,函数成员不计入,函数的代码在内存中只占据一份空间使用复制构造函数的情形:
用类的一个对象去初始化另一个对象时
当函数的形参是类的对象时(也就是值传递时),如果是引用传递则不会调用
当函数的返回值是类的对象时未定义复制构造函数系统会自动给出,并且会执行: 浅复制(复制对象在内存中的每一个bit)
浅复制是在复制这个对象的时候,让数据中的新旧对象指向同一个外部内容,析构的时候容易发生重复释放
深复制是在复制这个对象的时候,为数据中的对象制作了外部对象的独立复制。复制构造函数有且仅有一个
1
2
3
4
5
6// 复制构造,在定义对象的时候'='是传参的意思
A a;
A b(a) // 等价于 A b=a;
// 赋值
A a, b;
b = a;函数名后面加const表示为常函数,在该函数当中不可以修改变量
1
int getx() const { return x; }
函数参数尽量使用常引用而不是值,传递对象值会引起复制构造和析构,增加时间和空间开销
一个空类会自带四个函数: 构造函数,析构函数,复制构造函数,重载
析构函数由系统调用(不可以由程序调用),’~’不属于字母、数字或下划线,不是函数名‘&&a’: 右值引用
先构造的对象后析构(栈)
有生命的变量不一定能访问,局部定义静态变量在整个程序运行期间存在但是在主函数没法访问(private)
在类当中定义静态变量,数据改变只用修改一次,不用在每个对象都改一遍
类的静态数据成员为该类的所有对象共享,必须在类外定义和初始化(静态成员函数在类外定义不需要写static),用::指明所属的类
只有构造函数和复制构造函数有冒号初始化用法
静态成员函数不可以访问非静态成员(故静态成员函数中的函数也是静态成员函数!!!):因为静态成员函数是大家共有,非静态变量每个对象都有一个,如果通过类名+静态成员函数的方式调用的话不能确定输出哪一个对象的非静态变量
非友元函数访问私有成员的方法: 定义类的公有成员函数返回私有成员的值
A是B的友元,B不一定是A的友元,即友元关系是单向的
string类可以直接比较,不用字符串函数
全局变量和局部变量同名想要使用全局变量:::y(加全局作用域符)
定义类和函数的时候要注意顺序,如果类A要使用类B而类B的定义在类A之后,应该在类A之前先声明类B:class B; 函数同理
student stu[N];->对象数组已经被声明定义,只有靠赋值->stu[i] = student(id, score);
函数重载的要求(任选其一): 参数个数不同;参数类型不同;参数顺序不同;函数和自己的常函数
注意:编译器不会以
返回值
区分函数常对象只能调用常成员函数,普通对象可以调用常成员函数和普通成员函数,但是会优先调用普通成员函数
常数据成员只能通过构造函数的冒号初始化或者在类外初始化,不能通过赋值初始化
常引用只是名字是常量,但它所指向的数据并非常数据,例如:一个对象a,其常引用是b,那么通过b不可以修改该对象成员,但是用a可以改变
头文件用双引号兼容尖括号,双引号会先查本地路径再查系统路径
友元函数可以在类里面定义,并且不会将其当作成员函数
int *p = new int(5), 小括号是赋值!!!中括号才是数组
this指针是常量,不可以赋值
new和delete可以达到的功能比malloc和free更强,可以实现在生成一个新的对象指针的同时进行构造并传入参数,并且delete会自动析构对象
默认构造函数=缺省构造函数
继承不会吸收构造函数和析构函数,派生类的构造函数与析构函数要自己写或者使用系统默认
赋值通过派生类传入,如果要用到基类的方法或者属性要通过派生类传入(构造函数冒号传入)
如果基类中有默认构造函数,派生类可以不定义构造函数,都使用默认构造函数
使用const定义时必须赋初值(可以不是编译期间可见的值,比如int b = 25; const int a = b;)
如果一个类包含多个对象成员,对象成员的构造函数的调用顺序由它们在该类中的说明顺序决定,而它们在初始化表中的顺序无关
重载运算符以后运算符对重载的数据类型的原有操作被弃用,但是对于其他类型的数据仍然可以使用
函数形参中,基类形参包容派生类实参,派生类形参不包含基类实参(派生类形参可能含有新的属性或方法,但基类实参中不存在)
标识符,包括变量名、常量名、对象名、函数名、类型名
命名规则: 1.标识符用字母A-Z,a-z,数字0-9和下划线_构成; 2.以字母(大小写)或者下划线_开头,开头不可以使用数字; 3.不能与c编译系统已经预定义的、具有特殊用途的保留标识符(即关键字)同名。比如,不能将标识符命名为float,auto,break,case,this,try,for,while,int,char,short, unsigned,等等; 4.不允许出现标点字符,比如 @、& 和 %。
字符常量:
(1)\b:光标前移一位,同时会覆盖后面的内容 (2)\r:将光标移到本行开头 (3)\f: 在C++中,\f仅仅会打出一个小正方形□,代表换页符 (4)\v: 竖向跳格,就是将光标移到下一行的同样位置,但是,在C++中,\v照样会输出一个替代的符号□
枚举类型定义了一些整型符号常量的集合,
枚举类型的第一个常量默认值是0。 枚举类型常量的值允许彼此相同 虽然枚举类型常量是整数类型,但是不允许使用整数直接赋值给枚举类型变量,也不允许与整数进行运算,只可以与整数进行比较 在没有显式说明的情况下,枚举类型中的第一个枚举常量的值为0,第二个为1,以此类推。如果只指定了部分枚举常量的值,那么未指定值的枚举常量的值将依着最后一个指定值向后递增(步长为1)
真正编译后,编译器会加入 Run-time 库的 Startup 代码,它才是程序执行的真正入口。执行完 Startup 代码后,程序再调用用户代码的 main 函数执行。
Startup 代码中一般会获得命令行,获得进程句柄等。做好 main 函数的准备工作。而且不同的编译器,不同的平台,Startup 代码也不同。
在c++结构化程序设计框架中,程序的基本组成单元是函数
c++是面向过程和对象的程序设计语言
多态性是指一个面向对象的系统常常要求一组具有相同基本语义的方法能在同一接口下为不同的对象服务
面向对象的基本原理:使用现实世界的概念抽象地思考问题从而自然的解决问题
自顶向下
属于结构化程序设计方法,可复用不属于结构化程序设计方法面向对象程序设计主要考虑的是提高软件的
可重用性
正确的程序注释一般包含序言性注释和功能性注释
引用必须在定义时初始化,而且一旦初始化之后,就不能再成为其他变量的引用
输入缓冲区和输出缓冲区可以重叠:在dos窗口上输入的数据,都会被放在输入缓冲区中,但是输入进去时也会显示在输出缓冲区上
break对于while和for循环都是直接退出,应该使用continue表示进行下一次循环
c/c++实型数据:实型数据表示形式有两种,即十进制小数形式(十六进制、八进制二进制不对)和指数形式
一般形式:12.5、.6f(f表示浮点数类型)== 0.6
指数形式:E可以大写或小写,整数部分和小数部分可以省略其一,但不能都省略,得是整数(1E.02不对)函数重载的前提条件为不同函数实现的功能
完全相同
友元类之间的关系,不能传递,不能继承
被abstract修饰的类,称为抽象类,抽象类不能被实例化,即不能使用new创建一个对象,只能被继承
类的组合关系:Has-A;继承和派生:Is-A
一般是将非常短小的函数声明为
内联函数
(inline),且是否生效是由编译器
决定派生类中的虚函数会覆盖基类的虚函数,还会隐藏基类中同名函数的其他所有重载形式
STL中关联型容器都是
有序
的覆盖: 派生类中定义和基类
完全相同
的成员派生类指针不能用基类对象赋值;基类指针可以用派生类对象赋值; 看指针需要访问到的东西,赋值的数据是不是都有
派生类构造顺序:基类、对象成员、派生类本身->派生类多继承时按照对基类继承顺序执行初始化,再按照对象成员的说明顺序初始化最后执行派生类的初始化
派生类析构顺序与构造顺序相反
二义性问题: 派生类从不同方向继承同一个基类(菱形)
1
2
3
4解决方法:
虚基类:菱形第二层的两个基类虚继承顶层基类(class B1 : virtual public B), 构造第二个对象的时候引用第一次定义的最高基类
同名隐藏:在派生类中定义同名成员
类名限定带参构造函数: 顶部基类的参数是由底部派生类决定,中间的两个基类不起作用
多继承普通基类按继承顺序构造,多继承普通基类和虚基类,先构造虚基类
最派生类给最基类传了参数,中间基类也得在构造函数传参
初始化列表不会影响构造顺序,但是多继承的继承顺序会影响
类指针的输出会参考指针类型
传入对象
数组
,不可以用引用来接收,要用指针!纯虚函数也是虚函数,纯虚函数的
=0
不是赋值,是一个语法标识抽象类不能定义对象,但是
可以定义指针
(定义指针不用创建对象),也可以定义引用
(引用的是别的对象)有至少一个
纯虚函数
的类是抽象类抽象类只能作为基类,基类需要有构造和析构(派生类不会吸收基类的构造和析构函数)
抽象类是不完整的因为纯虚函数不完整(没有定义实现)
派生类继承抽象类,派生类只有一个函数,纯虚函数不算
1
2
3
4
5
6
7
8
9
10class Base
{
public:
virtual void display() = 0;
};
class Derived : public Base
{
public:
void display();
}; // 本来有两个display函数,但是纯虚函数不完整,所以只算一个虚函数是在派生类定义与基类完全相同的函数
虚函数必须是
非静态
的成员函数,静态成员函数无法区分是哪个对象, 构造函数不能是虚函数,析构函数可以(构造函数执行时对象还未创建好,析构函数执行时对象创建好了)运行时多态:Base *p; p->fun()//fun为虚函数 则无法确定调用哪个函数
基类指针和基类引用可以使用基类和派生类对象赋值,实现多态(基类
指针
或引用
+虚函数
->多态)绑定:确定执行哪个函数;绑定=联编;动态绑定 = 动态联编 = 运行时多态,静态绑定 = 静态联编 = 编译时多态
基类必须加virtual,派生类可加可不加
虚函数看对象类型,不是指针类型(运行时多态)
虚析构函数:防止使用基类析构函数析构派生类对象
Base *pb = new Derived; delete pb; // 删除只看指针类型进行析构,故只有Base的析构,会导致派生类对象无法析构,所以应该将基类的
析构函数
定义为虚函数
模板有两种,类模板和函数模板,类模板产生模板类,函数模板产生模板函数
一个函数/类模板可以生成无数多个模板函数/类
模板在编译后形成对应实例但是
模板消失
#include <bits/stdc++.h>
:通用的包含文件函数模板不再满足要求时可以重载函数
#include <cstdlib>
:c语言的stdlib.hs.getNum() = 8; // 如果返回的是私有成员的引用(需长期存在),则可以用这种方式修改私有成员
#include <assert>
:c语言的断言assert(num >= 0); // 如果num < 0则程序崩溃
深赋值:对象之间的深赋值需要重载”=”并对数组等数据重新申请内存并赋值,并且需要删除原有内容
if (this != & rhs) // 防止自我赋值
寻找素数:从2~sqrt(num)列出所有素数,如果num不可以被其中的任一素数整除,则num为素数