C++
第一章 导论
- 汇编语言:将机器指令映射为一些助记符。如ADD,SUB等。抽象层次低,需要考虑机器细节。
- 面向过程语言:就是分析出解决问题所需要的步骤,然后用函数把这些步骤一个个实现,使用的时候依次调用就行。
- C++的源程序是以【.cpp】为后缀的。即:C Plus Plus
- 面向对象语言:是把构成问题的事物分解成各个对象,建立对象的目的不是为了完成一个步骤,而是为了描述某个事物在整个问题的步骤中的行为。
- 类:抽象出同一类对象的共同属性和行为,形成类。
- 封装:隐蔽对象的内部细节,对外形成一个边界,只保留有限的对外接口,使用方便,安全性好。
- 继承:意义在于代码复用,改造,扩展已有的类。
- 多态:同样的消息作用在不同对象上引起不同的行为。
- 源程序:用源语言(高级语言)写的,有待翻译。
- 目标程序:完全的二进制机器语言。
- 可执行程序:连接目标程序以及库中的某些文件生成一个可执行文件。例如:.exe文件。
- 三种类型的翻译程序:
(1)汇编程序:将汇编语言源程序翻译成目标程序。
(2)编译程序:将高级语言源程序翻译成目标程序。一次翻译。
(3)解释程序:将高级语言源程序翻译成机器指令,它是边翻译边执行。 - 信息的存储单位
(1)位(bit,b):数据的最小单位,表示一位二进制信息。
(2)字节(byte,B):八位二进制数字组成。1byte = 8 bit。
(2)转换关系:1 KB = 1024 B。1 MB = 1024 K。 - 软件 = 程序+文档
- “<<”:流插入运算符,将内容插入到输出流中去。
- “>>”:提取运算符,作用在流对象cin上。
using namespace std;
命名空间,可以避免命名冲突。所有标准库里面的名字有一个命名空间,这个空间叫std。如果不用命名空间,每次使用标准库中的东西,都必须要std::
。- 字符串:C语言使用字符数组来存放字符串,C++推荐使用String类存放字符串。
- 全局变量会自动初始化,局部变量不会自动初始化。
- const定义符号常量:
- 语法:
const 类型名 常量名 = 常量值;
,必须初始化。例如:const double PI = 3.1415926;
。 - C语言中有#define定义的常量。语法:
#define PI 3.1415
。没有分号。const有分号。
- 语法:
'\n'
:表示换行,注意是单引号。- 调试:这是断点,执行逐过程(F10)。
第二章 Hello World
a + =3;
等价于a = a+3;
。x * = y+7;
等价于x = x*(y+7);
- 逗号运算符:连接两个表达式。先计算逗号左边的表达式,再计算右边,最终结果为表达式二的值。?
- 运算符优先级:“<,>,>=,<= ”的优先级高于“==,!=”。“!”高于“&&”高于“||”。
- 条件表达式:
x = a>b?a:b;
- sizeof运算:是求一个变量或一种数据类型所占的字节数。语法:
int len = sizeof(int);
。 - 位运算:
(1)按位与(&):将两个数字化成二进制,每一位对齐,进行与运算。用途:将某一位置0,其他位不变。例如:将char类型变量a的最低位置0:a = a & 0xfe;
。0xfe:11111110。
(2)移位(<<,>>):左移(<<):移位后,低位补0,高位舍弃。相当于乘以2。右移(>>):低位舍弃,高位如果是无符号就补0,有符号就补符号位。相当于除以2. - 隐式类型转换:低精度转换为高精度。
- 显式类型转换:语法:
类型转换操作符<类型说明符>(表达式);
。类型转换操作符:static_cast。例:static_cast<int> (z);
- 调试,设置断点,监视变量。
- C++没有输入输出语句,我们使用I/O流来输入输出。
cin>>a>>b;cout<<a<<b<<endl;
- 常用的I/O流类库操纵符:
#include<iomanip>
,就像C中的格式化一样。
操作符 | 含义 |
---|---|
dec | 数值数据采用十进制表示 |
hex | 数值数据采用十六进制表示 |
oct | 数值数据采用八进制表示 |
ws | 提取空白符 |
endl | 插入换行符,并刷新流 |
ends | 插入空字符 |
setprecision(int) | 设置浮点数的小数位数 |
setw(int) | 设置域宽 |
- 例如:
cout << setw(10) << setprecision(8) << 3.1415;
,cout <<oct <<10 << endl;
- if语句:实现选择结构。
- switch语句:如果case语句后面没有break,就会依次执行。语句最后是default语句,当所有case语句都不瞒足时,执行default语句。【注意】:如果某个case:后面不写语句,它就和下面的一个case共用一组语句。
- while语句:实现循环语句。避免死循环。
- do-while语句:至少执行一次。while和do-while因题选择。
- for语句:处理循环次数已知的循环。
- break语句和continue语句:break跳出最内存的循环。continue结束当前循环。
- const关键字:
(1)用来定义常量。语法:const int MAX = 23;
。
(2)用来定义常量指针:不可通过常量指针修改其指向的内容。语法:const int* p = &n;
。之后不能进行*p = 5;//编译出错
的操作,但是p可以再指向别的变量。p = &m;
是可以的。【注】不能把常量指针赋值给非常量指针(如果非要这么做,可以进行强制类型转换),反过来可以。
(3)定义常量指针的作用:用作函数参数,可以避免函数内部不小心修改参数指针所指的地方。
void fun(const char* p1)
{
strcpy(p1, "hoooo");
cout << p1 << endl;
}
- 自定义类型:为已有类型起别名。枚举类型就是自定义类型。
(1)typedef语句。语法:typedef 已有类型名 新类型名;
。例如:typedef double B;
。
(2)using语句。语法:using 新类型名 = 已有类型名;
。例如:using A = int;
。C++中的。 - 枚举类型:不能对枚举类型进行赋值(除非是在大括号内进行初始化),它是一个常量。整数值不能直接赋值给枚举变量,需要进行强制类型转换。枚举常量可以给整数值赋值。枚举集是整数的一个子集。语法:
enum num {a,b,c,d};//默认a=0,b=1······
,枚举类型是整数的子集。
#include<iostream>
using namespace std;
enum num{a=3,b,c,d};//默认a=0,后面累加1
int main()
{
num t;//定义枚举类型的变量
for (int i = a; i<=d;i++)
{
if (i == 0)
cout << "这是0" << endl;
else if (i == 1)
cout << "这是1" << endl;
else if (i == 2)
cout << "This is 2" << endl;
else
cout << "This is 3" << endl;
}
return 0;
}
- auto类型:编译器通过初始值自动判断变量的类型。例:
auto a = val1 + val2;
,a的数据类型取决于val1+val2的类型。 - decltype类型:定义一个变量与某一表达式的类型相同,但并不使用该表达式初始化变量。
decltype(i) j = 2;
,表示j以2作为初始值,类型与i一样。
第三章 函数
:表示为1e-15
。
整数相除结果为整数。如果想得到小数,可以这样写:double A = 1/ 2.0;
void srand(unsigned int seed);
,seed是产生随机数的种子。功能:为使rand()产生一系列伪随机数而设置一个起点。- 随机数:
int a = rand();
。如果不设置种子(没有用srand()函数),它生成的就是伪随机数(每次都一样)。设置种子之后,产生由种子生成的随机数。
int a = rand()%11;
,可以产生[0,10]之间的随机数。
- 函数的递归调用:函数自己调用自己。注意,递归出口很重要!!!递推–回溯
- 函数模板:
- 是指建立一个通用函数,其函数类型和形参类型都不具体指定,用一个虚拟的类型来代替。
- 凡是函数体相同的函数都可以用这个模板来代替不必定义多个函数,只在模板处定义一次就可以。
- 语法:
template<typename T>//可以用class替换typename
#include<iostream>
using namespace std;
template<typename T>//可以用class替换typename
T max(T a, T b)
{
if (a > b)
return a;
else
return b;
}
int main()
{
int x, y,max_i;
double c, d,max_d;
cin >> x >> y;
cin >> c >> d;
max_i = max(x, y);
max_d = max(c, d);
cout << "int型:" << max_i << endl;
cout << "double型:" << max_d << endl;
return 0;
}
- 函数的参数传递:单向传递、双向传递
(1)在函数被调用时才分配形参的存储单元。
(2)单向传递:值传递是传递参数值。值或对象。不能改变实参。
(3)双向传递:引用传递可以实现双向传递。【作用】:当需要返回多个值时使用。 - 引用类型:【作用】:引用可以作为函数的参数,实现双向传递。【注意】:和C的指针类似,但有区别。指针是指向地址,引用是使用别名。
(1)引用(&)是标识符的别名。相当于一个变量的别名。
(2)定义一个引用时,必须同时对它进行初始化,使它指向一个已存在的对象或变量。
(3)一个引用一旦被初始化,就不能改为指向其他对象。
(4)引用可以作为函数的返回值。
(5)作为常引用:int n; const int & r = n;
,r的类型就是const int &。不能通过常引用去修改其引用的内容。
(6)引用的使用范围:- 不能定义void类型的引用:如:
void &a = 9;
。 - 不能建立引用的数组。如:
char c[6]="hello";char &rc = c;
。 - 不能定义指向引用类型的指针变量。如:
int& *p = &a;
。 - 可以定义指针变量的引用。如:
int i = 5; int *p = &i; int* &pt = p;
。 - 可以使用const对引用进行限定,但不允许改变该引用的值。
int i = 5; const int &a = i; a = 10;//错误
,但是可以改变i的值:如:i=10;
。 - 可以用常量或表达式对引用进行初始化。如:
int a = 10; const int& b = a;//用a去初始化b。
- 不能定义void类型的引用:如:
int n=0;
int & Set()
{
return n;
}
int main()
{
Set()=40;
cout<<n;
return 0;
}//输出40
int a=4,b=5;
int &r=a;//r1是a的引用
r1=b;//是把b的值赋值给r1,也就是赋值给a。
#include<iostream>
using namespace std;
void swap1(int a, int b)//
{
int t;
t = a;
a = b;
b = t;
}
void swap2(int& a, int& b)//表示a和b是两个引用
{ //表示ab准备好了要给调用它的实参作别名。
int t;
t = a;
a = b;
b = t;
}
int main()
{
//int i, j;
//int &ri = i;//定义int引用ri,并初始化为变量i的引用。ri和i指向同一个东西
//j = 10;
//ri = j;//相当于i=j
int a = 5, b = 10;
cout << "值传递:" << endl;
cout << "a=" << a <<" "<< "b=" << b << endl;
swap1(a, b);
cout << "a=" << a <<" "<< "b=" << b << endl;
cout << "y引用传递:" << endl;
cout << "a=" << a << " " << "b=" << b << endl;
swap2(a, b);//调用时,和值传递调用一样
cout << "a=" << a << " " << "b=" << b << endl;
return 0;
}
- 作用域运算符:“::”,当全局变量和局部变量重名是,可以使用“::”l来访问全局变量。
- 含有可变参数的函数:C++有两种方法
(1)如果所有的的实参类型相同,可以传递一个名为initializer_list的标准库类型。
(3)initializer_list:是一个类模板,用于表示某种特定类型的值的数组。头文件:#include<initializer_list>
(4)使用initializer_list时,要在后面加一对尖括号,写出类型参数。例:initializer_list<String> ls;
,表示initializer_list的元素类型都是String类型的,就是说,它包括了一组String构成的参数表。
(5)initializer_list的对象中的元素永远是常量值。
(6)含有initializer_list类型的形参的函数也可以同时拥有其他类型的形参。
(7)initializer_list使用列举:可以使用initializer_list编写一个错误信息的输出函数(异常处理)。
(2)如果实参的类型不同,我们可以编写一个可变参数的模板。 - 内联函数:为了减少函数调用的开销。【作用】:在调用简单函数时提高效率。
(1)定义:在函数定义前面直接加【inline】关键字。
(2)执行过程:不产生调用语句,直接把内联函数的代码插入到调用的地方。
(3)注意:内联函数体内不能有循环语句和switch语句。内联函数的定义必须出现在内联函数第一次被调用之前(使编译器在调用之前就见过它)。对内联函数不能进行异常接口声明。
inline int fun(int a,int b)
{
if (a > b)return a;
else return b;
}
- CONSTEXPR函数:用来初始化常量的,给的参数是常量,它的返回值一定是常量。
- 函数的缺省参数:定义函数的时候可以让最右边的连续若干个参数有缺省值,在调用函数时,若相应位置不写参数,那参数就是缺省值。
(1)目的:提高程序的可扩充性。即:如果写好的函数要添加新的参数,而原先那些调用该函数的语句,未必需要使用新增的参数,就可以使用缺省参数来避免对原来那些函数调用语句的修改。
(2)如果函数声明在调用之前,可以在声明处给出函数的默认参数,下面定义函数时,就不能再给默认值了,即使一样也不行。如果函数定义在调用之前,可以直接给默认参数,如下。
void func(int a,int b = 1,int c = 3)
{
```
}
//调用
func(10);//相当于func(10,1,3);
func(10,8);//相当于func(10,8,3);
func(10,,9);//出错,只能最右边的连续缺省。
-
函数重载:多个函数名字相同,函数的参数个数或参数类型不同。不能以函数返回值来区别函数。即:参数列表不同。静态的多态性机制,在编译是就实现。
-
代码调试时,使用“逐语句(F11)功能”,进入调用函数的内部。
-
比较两个浮点数是否相等时用abs()函数。看两个数的差的绝对值是否小于一个很小的数。
-
C语言中的动态内存分配是通过malloc和free关键字实现的。
-
C++的动态内存分配:用new和delete代替malloc和free。
(1)用new运算法实现动态内存分配- 第一种用法,分配一个变量。例:p = new T;。T是任意类型名,P是类型为T*的指针。动态的分配出一片大小为Sizeof(T)字节的内存空间,并把该内存空间的起始地址赋值给P。例:
int *a = new int;
- 第二种用法:分配一个数组。例:p = new T[];。T为任意类型名,P是类型为T*的指针,N是要分配的数组元素的个数。动态的分配出一块大小为NxSizeof(T)子节的内存空间,并把该内存空间的起始地址赋值给P。如:
int *p = new int[10];
(2)使用delete运算符释放动态分配的内存。
- 用“new”动态分配的内存空间,一定要用“delete”运算符来释放。释放操作只能进行一次。
- 语法:
delete 指针;//该指针必须是被new出来的空间
- 释放动态分配的数组。语法:
delete [] 指针;
。
- 第一种用法,分配一个变量。例:p = new T;。T是任意类型名,P是类型为T*的指针。动态的分配出一片大小为Sizeof(T)字节的内存空间,并把该内存空间的起始地址赋值给P。例:
第四章 类和对象
1 结构体
2 联合体
3 枚举类扫描二维码关注公众号,回复: 9949877 查看本文章
- 面向对象=类+类+类+……+类。
- 定义对象时要用【构造函数】对对象进行初始化。
- 删除对象时要用【析构函数】释放资源。
面向对象的特点:
- 抽象:对同一类对象的共同属性和行为进行概括,形成类。
- 封装:将抽象出来的数据,代码封装在一起,形成类。通过类声明中的大括号{}实现封装。
- 多态:同一个名称,不同的功能实现方式。【目的】:达到行为标识统一,减少程序中标识符的个数。
- 继承:在已有类的基础之上进行扩展,形成新的类。
4.1 类和对象的定义
- 对象是类的实例化。定义类的对象,才可以通过对象使用类中定义的功能。
- 类和结构体变量一样,对象所占用的内存空间的大小等于所有成员变量的大小之和。
- 使用类的成员变量和成员函数:
(1)用法一:对象名.成员名
(2)用法二:指针->成员名。(就像C语言中指针一样,定义一个该类型的指针指向该变量)
(3)用法三:引用名.成员民。 - 类成员的可访问范围:
修饰符 | 含义 |
---|---|
public | 公有成员,可以在任何地方访问 |
private | 私有成员,只能在类内访问 |
protected | 保护成员,<待更新> |
- 定义类的语法:使用calss关键字。
class 类名称
{
public:
公有成员(外部接口)
private:
私有成员
//int a = 1;//类内的初始值
protected:
保护型成员
};
- 如果没有写修饰符,默认是private。
- 设置私有成员的机制,叫做隐藏。隐藏的目的是强制对成员变量的范围一定要通过成员函数进行。
- 成员函数可以重载也可以有缺省函数值。
- 可设置类内的初始值,如果构造函数没有对该属性进行初始化,就使用类内的初始值,如果构造函数对其初始化,就使用构造函数的初始值。
private
修饰的成员,只允许本类中的函数使用。类外部任何函数都不能访问(除非手动授权)。- 定义对象的语法:
类名 对象名;
,例:Clock myClock;
。 - 类中的成员之间使用成员名来访问,类外访问成员要使用“对象名.成员名”的方式来访问public成员。
- 类的成员函数:
(1)在类中声明函数原型,在类外给出函数体实现,并在函数名前使用类名加以限定。语法如下:void man::fun(int x, int y, int z)
(2)也可以直接在类体中给出函数体,形成内联成员函数。【注意】:对于简单的函数可以声明为内联函数形式,内联函数体中不要有复杂的结构(循环和switch语句)。【方式】:1. 将函数体放在类中。2. 使用inline关键字,在类体中声明函数原型,在类外实现函数,用inline。
(3)允许声明重载函数和带默认参数的函数。
#include<iostream>
#include<cmath>
using namespace std;
class man
{
public:
int a,b,c;
void fun(int a=1,int b=2,int c=3);//声明函数原型,带默认参数
void dis();
};
void man::fun(int x, int y, int z)//函数的实现,
{ //如果在下面调用函数时不给参数,就使用默认参数
a = x;
b = y;
c = z;
}
void man::dis()
{
cout << a << " " << b << " " << c << endl;;
}
int main()
{
man a;
a.fun();
a.dis();
return 0;
}
4.2 构造函数
- 对对象进行初始化,用到构造函数。
- 如果我们没有定义构造函数,编译器会自动加一个默认的无参的构造函数。
- 构造函数:
(1)类体中特殊函数,用来描述对象的初始化算法。
(2)规则:函数名与类名一样,不能定义返回值类型,不能有return语句,可以有形参,也可以没有,可以是内联函数,可以重载,可以带默认参数。
(3)构造函数在对象创建时被自动调用。例:Clock myClock(1,2,3);
。
(4)不带参数的构造函数和带默认参数的构造函数不能同时出现。
(5)= default
;:如果程序已经定义了构造函数,但是我们希望编译器隐含默认构造函数,就可以使用= default;
。例:Clock()=default;
。 - 构造函数在对象数组中的使用:
(1)例:A arr[2];A arr_2[3] = { 2,5 };
- 第一个数组arr调用无参的默认构造函数去初始化两个对象。
- 第二个数组arr_2是调用一个参数的构造函数。用2去初始化arr[0],用5去初始化arr[1],用0去初始化arr[2]。
- 指针数组里面的元素是指针。没有初始化,就不会生成。
- 构造函数也可以看做一个类型转换构造函数:
(1)例:
class Demo
{
public:
int a;
Demo(int i)
{
a=i;
}
};
Demo d;
d = 4;
(2)会将4自动转换为一个临时对象赋值给d。这个临时对象在执行完这句后就会自动调用析构函数。
(3){}是一个变量的生存期的范围界限。{}内的对象,在出这个{}时,就自动调用析构函数。
(4)静态局部变量(对象)在函数调用结束时仍然保持其结果。直到整个程序结束。【static】就是静态的意思。
(5)先初始的,后析构。
#include<iostream>
#include<cmath>
using namespace std;
class man
{
public:
int a,b,c;
man(int x, int y, int c);//声明构造函数,不能有返回值类型,直接写类名就可
man();//默认构造函数
void dis();//输出函数
};
//构造函数的实现,注意格式
man::man(int x, int y, int z):a(x), b(y), c(z) {}
//默认构造函数实现
man::man() : a(0), b(1), c(0) {}
void man::dis()
{
cout << a << " " << b << " " << c << endl;;
}
int main()
{
man A(12,13,14);//实例化对象,自动调用有参构造函数
A.dis();//输出a,b,c。
man B;//调用无参的默认构造函数
B.dis();
return 0;
}
【注意】:构造函数有两种类型:
- 一种是有冒号的,在冒号后面进行初始化。
- 另外一种是没有冒号的,在函数体中进行初始化。
两种的区别:
- 冒号初始化是给数据成员分配内存空间时就进行初始化。就是说,在进入下面的函数体之前,这个数据成员就已经被初始化。用于常量和引用的初始化。
- 在函数体中初始化,是在所有的数据成员分配完内存空间之后才进行初始化。这个不能用于常量和引用的初始。因为常量和引用在定义时就必须被初始化。
如果不是常量和引用的数据类进行初始化,这两种没有区别。注意语法。
如果是对数组进行初始化,则应当在函数体内进行。
-
委托构造函数:使用类的其它构造函数执行初始化过程。【优点】:使代码容易修改。
-
复制构造函数:【作用】:用一个已存在的对象去初始化同类型的另外一个对象。
(1)用已存在的对象的引用做另外一个的参数。
(2)复制构造函数是一种特殊的构造函数,其形参为本类对象的引用(别名,用&)
(3)复制函数被调用的三种情况:- 定义一个对象时,即:以本类的另外一个对象作为初始值,发生复制构造
- 如果函数的形参是类的对象,调用函数时,将使用实参对象初始化形参对象(就是普通调用函数时,将实参赋值给形参),发生复制构造
- 如果函数的返回值是类的对象,函数执行完成返回主调函数时,将使用return语句中的对象初始化一个临时无名对象,传递给主调函数,发生复制构造。
(4)如果没有写复制构造函数,编译器会自动生成一个复制构造函数。【功能】:用初始值对象的每个数据成员,去初始化将要建立对象的对应的数据成员。
(5)如果不希望对象被复制,使用“=delete;”指示编译器不生成默认构造函数。man(const man &p)=delete;
(6)对象之间的赋值不会调用复制构造函数。(赋值和初始化是不一样的)
(7)对象作函数参数,会调用复制构造函数,这样会产生较大的开销。所以,就可以使用引用类型作为为参数。如果想要确保实参值不会在被调函数体内被修改,可以使用常引用。const &。
class 类名
{
public:
类名(形参);//构造函数
类名(const 类名 &对象名);//复制构造函数,使用const可以避免改变原有对象,常引用。
}
类名::类名(const 类名 &对象名)
{函数体}
//复制构造函数的使用
#include<iostream>
#include<cmath>
using namespace std;
class Man
{
public:
int a,b,c;
Man(int x, int y, int c);//声明构造函数,不能有返回值类型,直接写类名就可
Man(const Man &m);//如果不写,编译器会自动生成
void dis();//输出函数
};
//构造函数的实现,注意格式
Man::Man(int x, int y, int z):a(x), b(y), c(z) {}
void Man::dis()
{
cout << a << " " << b << " " << c << endl;;
}
//复制构造函数实现,可以不写,编译器会自动生成。
Man::Man(const Man &m)
{
a = m.a + 1;//我们不直接复制,做一些改变
b = m.b + 1;
c = m.c + 1;
}
void fun1(Man A)//形参为对象,将实参复制给形参发生复制构造函数
{
cout << "A.a=" << A.a << endl;
}
Man fun2()//返回值为对象
{
Man m(1,2,3);
return m;
}
int main()
{
Man A(12,13,14);//实例化对象,自动调用有参构造函数
cout << "对象A:";
A.dis();//输出a,b,c。
Man B(A);//用A来初始化B。第一次调用复制构造函数
cout << "用对象A初始化之后的对象B:";
B.dis();//输出的值比A的对应多1
cout << "对象B的a值:";
fun1(B);//对象B作函数参数,第二次调用复制构造函数
Man C(0, 0, 0);
cout << "对象C的a值:";
C = fun2();//函数返回值是对象,第三次调用复制构造函数
cout << "C.c = " << C.c << endl;
return 0;
}
4.3 析构函数
- 一个对象在实例化时,它可能会占用很多系统资源,所以,在用完之后,可以使用析构函数进行资源释放。
- 如果程序中未声明析构函数,编译器会自动生成一个默认的析构函数,它的函数体为空。
- 析构函数:
(1)语法:~类名();
(2)析构函数没有参数,没有返回类型。
~Man();//析构函数的声明
//析构函数的实现
Man::~Man()
{
}
- new出来的对象,不使用delete就不会自动析构。所以,这里是p的两个对象和a被析构。p2不会自动调用析构函数。
4.4 类的组合
- 一个类的对象可以作为另外一个类的成员。(相当于一个大的零件中加一个小零件)
- 组合类的构造函数:不仅要对本类中的基本类型成员数据初始化,也要对对象成员初始化。
- 有成员对象的类叫做封闭类。
(1)任何生成封闭类对象的语句,都要让编译器明白,对象中的成员对象是如何初始化的。
(2)具体做法就是:通过封闭类的构造函数的初始化列表。 - 语法:
类名::类名(对象成员所需形参,本类对象形参):对象1(参数),对象2(参数)……
{
//函数其他语句
}
【注意】:在定义类时,尽量注意要多写一个默认无参构造函数,使得类可以被广泛使用(作为另外一个类的成员时,如果组合类不写构造函数,那这个成员对象,就只能调用自己的默认无参构造函数,这时,如果我们给这个类没有定义无参默认构造函数,即:都是有参数的构造函数,编译器就会报错)。
-
构造组合类对象时的初始化次序:
(1)首先对构造函数初始化列表中列出的成员(基本类型成员和对象成员)进行初始化,成员在类体中定义的次序:- 先执行所有对象成员的构造函数,然后执行封闭类的构造函数。
- 成员对象构造函数的调用次序:先声明者先构造
- 初始化列表中未列出的成员对象,调用默认无参构造函数进行初始化。
(2)出来完初始化列表之后,在执行构造函数的函数体。
(3)在封闭类的对象消亡时,先执行封闭类的析构函数,然后再执行对象成员的析构函数。
#include<iostream>
using namespace std;
class Node
{
public:
int x, y;//点的xy坐标
Node();//无参构造函数
Node(int a, int b);//有参构造函数
Node(const Node& node);//Node的拷贝构造函数
};
//Node无参构造函数实现
Node::Node() : x(0), y(0)
{
}
//Node有参构造函数
Node::Node(int a, int b) : x(a), y(b)
{
cout << "Node的有参构造函数" << endl;
}
//Node的拷贝构造函数的实现
Node::Node(const Node& node) : x(node.x), y(node.y)
{
cout << "Node的拷贝构造函数" << endl;
}
class Line
{
public:
Node st, end;
Line(Node p1, Node p2);//线段的有参构造函数
Line(const Line& L);//Line的拷贝构造函数
void showLine();//划线函数
};
//线段的有参构造函数
Line::Line(Node p1, Node p2) :st(p1), end(p2) {}
Line::Line(const Line& L) : st(L.st), end(L.end)
{
cout << "Line的拷贝构造函数" << endl;
}
void Line::showLine()
{
cout << "起点:" << "x=" << st.x << ',' << "y=" << st.y << endl;
cout << "终点:" << "x=" << end.x << ',' << "y=" << end.y << endl;
}
int main()
{
Node p1(1,1), p2(9,9);
Line L(p1, p2);//从后往前
L.showLine();
Line L2(L);//用L去初始化L2
L2.showLine();
return 0;
}
输出:
Node的有参构造函数
Node的有参构造函数
Node的拷贝构造函数
Node的拷贝构造函数
Node的拷贝构造函数
Node的拷贝构造函数
起点:x=1,y=1
终点:x=9,y=9
Node的拷贝构造函数
Node的拷贝构造函数
Line的拷贝构造函数
起点:x=1,y=1
终点:x=9,y=9
【代码说明】:
在执行
Line L(p1, p2);
这一句时,是先进行形实结合(把主函数的p1,p2与Line::Line(Node p1, Node p2) 的p1,p2形实结合,先p2再p1),调用两次Node拷贝构造函数,再进行st(p1), end(p2)
的初始化,这的初始化又是用对象去初始化,所以,又调用两次的Node拷贝函数。
- 【两个类相互引用】:用到前向引用声明
- 类必须先声明后使用,如果需要在某个类的声明之前就引用该类,则应进行前向引用声明。前向引用声明只为程序引入了一个标识符,但具体声明在其他地方。
(1)注意事项:- 在提供一个完整的类声明之前,不能声明该类的的对象,也不能在内联函数中使用该类的对象。
- 当使用前向引用声明时,只能使用被声明的符号,而不能涉及类的细节。
class B;//前向引用声明
class A
{
public :
void fun(B b);//参数为B类型
};
class B
{
public :
int a;
void fun(A a);
};
- 【UML】:
- UNL有三个基本的部分:
(1)事物(Things)
(2)关系(Relationship)
(3)图(Diagrams)
4.5 静态成员
什么是静态成员?
-
静态成员:在说明前面加了static关键字修饰的成员。例:
static int a;
。有静态成员变量和静态成员函数。
(1)静态成员变量:为每个对象共享。sizeof不会计算静态成员变量的大小。
(2)静态成员函数:普通函数必须具体作用于某个对象,即:通过对象来调用函数。静态成员函数并不具体作用于某个对象。
(3)静态成员不需要对象就可以访问。普通的访问方法也是可以。【类名::成员名】
(4)静态成员变量本质上是全局变量,哪怕一个对象都不存在,类的静态成员变量也存在。
(5)静态成员函数本质上是全局函数。
(6)设置静态成员的目的:- 将和某些类紧密相关的全局变量和函数写到类中,看起来像一个整体,易于维护和理解。
(7)所有的静态成员变量必须拿到所有的函数外面进行声明或初始化。
(8)在静态成员函数中,不能访问非静态的成员变量,也不能调用非静态的成员函数。 -
常量对象:使用const关键字定义的对象。例:
const Demo obj;
。
(1)常量对象上面不能使用非常量的成员函数。 -
常量成员函数:在类的成员函数说明后面可以加上const关键字,则该成员函数成员常量成员函数。例:
void Demo::fun1() const {……}
(1)特点:常量成员函数在执行期间不应修改其所作用对象。因此,在常量函数中不能修改成员变量的值(静态成员变量除外),也不能调用同类非常量的成员函数(静态函数除外)。
(2)两个同名的函数。一个函数是常量函数,另外一个不是,这也算是重载。
4.5 结构体
- C++的结构体是一种特殊的类
(1)类的默认访问权限是private。
(2)C++结构体的默认访问权限是public。 - C++结构体的使用:
(1)定义主要用来保存数据,而没有太多的函数(可以有函数)。
(2)将一些数据集成在一起,可以在外部很方便的访问。 - 结构体的定义:
struct 结构体名 {
公有成员//默认
protected:
保护型成员
private:
私有成员
};
-
结构体的初始化:
(1)如果:- 一个结构体的全部数据成员都是公有的。
- 没有用户定义的构造函数。
- 没有基类和虚函数。
(2)初始化:类型名 变量名 = {成员数据1的初始值,成员数据2的初始值,……};
4.6 联合体
- 联合体的目的是存储空间的共用。
- 联合体的定义:
union 联合体名称 {
公有成员
protected:
保护型成员
private:
私有成员
};
- 联合体的特点:
(1)成员公用同一组内存单元。
(2)如何两个成员不会同时有效。(比如定义性别时) - 无名联合体:只是说明联合体内的成员公用一个存储单元,同一时间只能有一个有效
union {
int i;
int j;
};
//在程序中直接像普通的变量使用。如下:
i = 10;
j = 2.2;
4.7 枚举类
- 枚举类也称为强类型的枚举
- 语法:enum class 枚举类型名 : 底层类型 {};。这里的底层类型默认是int型。
- 枚举类的优势:
(1)强作用域:使作用域限制在枚举类中。使用时:枚举类型名::变量。
(2)转换限制:枚举类对象不可以与整型隐式的相互转换。
(3)可以指定底层类型。
#include<iostream>
#include<string>
using namespace std;
enum CPU_Rank{p1=1,p2,p3,p4,p5,p6,p7};
enum RAM_Type { DDR2=2, DDR3, DDR4 };//RAM类型
class CPU
{
private:
CPU_Rank rank;
string name;//名字
public:
CPU(CPU_Rank r, string name_1);//CPU构造函数
~CPU();//析构函数
void run() { cout << "CPU开始运行" << endl; };
void stop() { cout << "CPU停止运行" << endl; };
};
CPU::CPU(CPU_Rank r1, string name_1) :rank(r1), name(name_1)
{
cout << "构造CPU" << endl;
};
CPU::~CPU()
{
cout << "析构CPU" << endl;
}
class RAM
{//容量,类型,主频
private:
int capa;//容量
RAM_Type ty;//类型
int a;//主频
public:
RAM(int cpap_1,RAM_Type ty_1);//构造函数
~RAM() { cout << "析构了一个RAM" << endl; };//析构函数
void run() { cout << "RAM开始运行" << endl; };
void stop() { cout << "RAM停止运行" << endl; };
};
RAM::RAM(int cpap_1, RAM_Type ty_1) :capa(cpap_1), ty(ty_1)
{
cout << "构造了一个RAM" << endl;
}
class COMPUTER
{
private:
CPU my_cpu;
RAM my_ram;
int size;//机械硬盘,GB
public:
COMPUTER(CPU c, RAM r, int s);
void Run();
void Stop();
};
void COMPUTER::Run()
{
my_cpu.run();
my_ram.run();
cout << "computer开始运行" << endl;
}
void COMPUTER::Stop()
{
my_cpu.stop();
my_ram.stop();
cout << "computer停止工作" << endl;
}
COMPUTER::COMPUTER(CPU c, RAM r, int s) :my_cpu(c), my_ram(r), size(s)
{
cout << "构造一个computer" << endl;
}
int main()
{
CPU a(p1, "英特");
a.run();
a.stop();
cout << "………………\n" << endl;
RAM r(1024, DDR3);
r.run();
r.stop();
cout << "………………\n" << endl;
COMPUTER com(a, r, 2048);
com.Run();
com.Stop();
return 0;
}
- string 类:字符串
(1)s == t
:判断字符串s和t是否相等
(2)s < t
:判断字符串s是否小于t,按字典序判断
(3)s + t
:连接两个字符串
(4)s[i]
:访问串中下标为i的字符 - 字符串数组:
string name[10];
。代表10个字符串元素 - getline():可以出入整行字符串。(头文件:string)
(1)getline(cin,str):从键盘输入一行字符串,包括空格。以换行作为结束符
(2)getline(cin,str,’,’):以“,’”作为结束标记,遇到“,”时,表示输入结束。
静态成员变量和
- 静态成员:使用static关键字定义的成员变量。
·················································每周更新···············································