1. C++的前世今生
1.1 延续了40年还 未过时的开发语言
C++是一门面向对象的语言,属于C语言的一个超集(超集的意思就是C语言有的C++全有,但是C++
有的C语言没有),支持过程化变成,支持面向对象编程,泛型编程
C++之父 Bjarne Stroustrup
C++与其他语言的比较
语言 内存管理 继承 多态 面向对象 编译机制
C++ 指针,手动管理 面向过程 编译,链接,可执行文件
面向对象
JAVA 引用 纯粹的面向对象
1.2 C++的优势
简洁 编写风格自由
效率高 适合底层系统开发
跨平台 可移植性强
复用性强 泛型编程
1.3 C++常用IDE
VC++6.0 DEV C++ VS2017 GCC/G++(LINUX)
2. C++比C多了哪些特性
尽可能的与C靠近,但又不过分的近!
(1)非类部分的扩充
基本输入输出
引用
new和delete运算符
函数
(2)面向对象的程序设计
(3)变量的范围
所有我们要使用的变量都必须事先声明过。C和C++语言的一个重要区别是,在C++语言中我们
可以在源程序中 任何地方声明变量,甚至可以在两个可执行语句的中间声明变量,而不像在
C语言中变量声明只能在程序的开头部分。
但是为便于管理,C++也应该把变量声明放在头部。
2.1 C++新增的数据类型:
auto、long long(8个字节)、bool、wchar_t(2个字节,用来承载unicode字符集)宽字符、
class类类型、&引用类型
2.2 新增关键字:
asm try inline bool dynamic_cast typeid typename
mutable catch explicit namespace static_cast
export new using class operator virtual FALSE private
template const_cast protected this public throw wchar_t
friend TRUE delete reinterpret_cast
2.3 面向对象特性(重点)
类(封装、继承、多态、重载、抽象)
对象
2.4 泛型编程特性(重点)
模板、泛型、模板元编程
2.5 结构体
C的结构体内不允许有函数存在,C++允许有内部成员函数,且允许改函数是虚函数。所以C的结构体
是没有构造函数、析构函数、和this指针的。
事实上,结构体就是一种公有的默认访问权限为public的类,特殊的类,
C的结构体对内部成员变量的访问权限只能是public,而C++允许public、protected 、private三种
C语言的结构体是不可以继承的,C++的结构体是可以从其他的结构体或者类继承过来的。
2.6 其他细节
C++的注释 // /**/
C++的iostream替代C的stdio
C++定义块的消除,声明块不必在语句执行块的前面(C语言的 声明必须在执行语句的前面)
C++增加安全强制转换static_cast、const_cast、dynamic_cast、reinterpret_cast
C++定义变量时struct可省略(C语言是不可以省略的)
C++名字空间namespace
C++对enum严格的类型检查、void*不能直接赋值给某个指针,而需要强制转换
3. typid 关键字
获取一个表达式的类型
typeid()操作符的作用就是获取一个表达式的类型。返回结果是const type_info&类型
使用方法:
typeid(类型或变量或表达式).name() 返回类型名字字符串
#include<iostream>
using namespace std;
struct A{};
int main()
{
//直接输出类型名称
cout<<typeid(int).name()<<endl; //输出int
//直接输出类型名称
cout << typeid(A).name() << endl; //输出struct A
return 0;
}
4. c++新增类型
wchar_t bool longlong auto类型
4.1 wchar_t类型
wchar_t类型主要用在国际化程序的实现中,unicode编码的宽字符一般以wchar_t类型存储。
wchar_t与char的区别
类型 长度 表示能力 赋值
char 8bit 最多只能包含256种字符 'A'
wchar_t 16bit wchar_t所能表示的字符数远超char型 L'B'
#include "pch.h"
#include <iostream>
using namespace std;
int main()
{
//单字节字符,1个字节
char a = 'A';
//宽字节字符,2个字节
wchar_t b = L'B';
wchar_t c = L'中';
cout << a << " " << sizeof(a) << endl;
cout << b << " " << sizeof(b) << endl;
cout << c << " " << sizeof(c) << endl;
//std::cout << "Hello Jarry!\n";
return 0;
}
结果:
A 1
66 2 //怎么让这个输出字符呢?
20013 2
#include "pch.h"
#include <iostream>
using namespace std;
int main()
{
//单字节字符,1个字节
char a = 'A';
//宽字节字符,2个字节
wchar_t b = L'B';
wchar_t c = L'中';
cout << a << " " << sizeof(a) << endl;
//宽字符输出用wcout
wcout << b << " " << sizeof(b) << endl;
wcout << c << " " << sizeof(c) << endl;
//std::cout << "Hello Jarry!\n";
return 0;
}
结果:
A 1
B 2
汉字“中”没有出现
注意:C++默认的Locale是EN_US,而且一般终端(尤其是Windows下的)不支持Unicode.
所以需要将wchar_t转化为本地编码,wcout.imbue(locale("chs"));
#include "pch.h"
#include <iostream>
using namespace std;
int main()
{
//单字节字符,1个字节
char a = 'A';
//宽字节字符,2个字节
wchar_t b = L'B';
wchar_t c = L'中';
cout << a << " " << sizeof(a) << endl;
//宽字符输出用wcout
wcout << b << " " << sizeof(b) << endl;
//默认Locale是EN_US,需要设置为chs
wcout.imbue(locale("chs"));
wcout << c << " " << sizeof(c) << endl;
//std::cout << "Hello Jarry!\n";
return 0;
}
结果:
A 1
B 2
中 2
4.2 bool类型
布尔类型,占一个字节,0为假,非0为真,用于条件判断。false/true是标准C++语言里新增的关键字,
他们是bool类型。
#include "pch.h"
#include <iostream>
using namespace std;
int main()
{
//布尔值有两种 0或1
bool b1 = true;
bool b2 = false;
bool b3 = 0; //0为假
bool b4 = -1; //非0为真
cout << b1 << endl;
cout << b2 << endl;
cout << b3 << endl;
cout << b4 << endl;
cout << "size:" << sizeof(b1) << endl;
return 0;
}
结果:
1
0
0
1
size:1
布尔类型占1个字节
4.3 long long类型
long占四个字节,longlong占8个字节,实现了在32机器上可以扩展8个字节的数据。
#include "pch.h"
#include <iostream>
using namespace std;
int main()
{
long c1 = 4294967296;//2^32
cout << sizeof(c1) << " " << c1 << endl;
return 0;
}
结果:
4 0
结果为0说明已经溢出
4.4 auto类型
C++11标准引入了auto类型说明符,它能让编译器自动分析表达式的类型。
#include "pch.h"
#include <iostream>
using namespace std;
int main()
{
auto d1 = 99; //需要初始值才能推导
auto d2 = 'B';
auto d3 = L'C';
auto d4 = 12.1f;
auto d5 = 12.345;
auto d6 = &d5;
cout << typeid(d1).name() << endl;
cout << typeid(d2).name() << endl;
cout << typeid(d3).name() << endl;
cout << typeid(d4).name() << endl;
cout << typeid(d5).name() << endl;
cout << typeid(d6).name() << endl;
return 0;
}
结果:
int
char
wchar_t
float
double
double *
5. _asm关键字
在C++中嵌入汇编代码,实现高级语言和低价语言混合编程
_asm关键字用于调用内联汇编程序,可在C或C++代码中出现。它后面可以跟一个程序
指令、一组在大括号中的指令或者一对空大括号。
为什么要汇编?
因为在某些时候汇编的效率比C和C++还要高。对应对效率要求比较高的功能我们需要汇编
来实现。
#include "pch.h"
#include <iostream>
using namespace std;
int main()
{
int a = 111;
int b = 222;
int sum = 0;
__asm //下面是内嵌汇编
{
mov eax, a; //a的值给eax寄存器
mov ebx, b;
lea eax, [eax + ebx];
mov sum, eax; //可以直接将eax的值给到sum
}
cout << sum;
return 0;
}
结果:
333
6. 引用类型
为变量取了一个别名
引用类型,如果为某个变量分配一个引用类型,则该变量 将引用(或“指向”)原始值,
就好像为它创建了一个终身别名。效果类似于指针。
许文强,江湖人称强哥,引用就好像为它创建了一个别名(强哥),那么说强哥指的就是许文强。
引用的规则:
(1)引用被创建的同时必须被初始化
(2)无NULL引用,引用必须与合法的存储单元关联
(3)一旦引用被初始化,就不能改变引用的关系。一旦初始化,终身绑定在一起。
指针与引用的区别
类别 创建时必须初始化 值是否可变 能否为NULL 其他
指针 可以不初始化 可变 可以 有指针的指针
引用 必须初始化 一旦初始化,不可变 不可以 无引用的引用
7. C++头文件
C++头文件不带.h后缀的
#include <iostream> //数据流输入输出
#include <string> //字符串类
#include <queue> //STL队列容器
C语言风格的头文件(带.h)
在VS2017编译器中,我们没有包含stdio.h照样可以使用printf()函数,是因为
VS2017自动包含了cstdio,在外部依赖中cstdio中已经把#include <stdio.h>添加进来了
#include <pch.h>
#include <iostream>
using namespace std;
int main()
{
printf("Hello world!");
return 0;
}
如果有的编译中没有自动包含cstdio,就需要在.cpp中手动加上
#include <pch.h>
#include <iostream> //C++的头文件,不带.h
#include <cstdio> //C的头文件,去掉.h,前面加上c
#include "008.h" //如果是自定义头文件,需要加.h
using namespace std;
int main()
{
printf("Hello world!");
return 0;
}
同理其他的如
#include <cstdio> //定义输入/输出函数
#include <cstring> //字符串处理
#include <cstdlib> //定义杂项函数及内存分配函数
头文件包含中<>与“”的区别
#include <>
如果此文件被认为是标准的头文件,我们便以<>将文件名括住,
编译器搜索此文件时会在默认的系统目录中寻址。
#include ""
如果头文件时用户自己定义的,我们便以""将文件名括住,编译器
搜索此文件时会从当前工程所在目录开始寻找。
头文件都写些什么东西?
1. .h头文件中,只能存放变量或者函数的声明,而不要放定义。
比如:
extern int a; //变量声明
void f(); //函数声明
如果写上int a;或者void f() {}这样的语句,那么一旦这个头文件被两个以上的
.cpp文件包含的话,编译器会立马报错。
2. 内联函数
3. 定义类
如果头文件中只包含声明语句的话,它被同一个.cpp文件包含再多次都没有问题,因为
声明语句的出现次数是不受限制的。
8. 面向对象编程OOP
8.1 面向过程编程POP:面向过程编程(Procedure Oriented Programming)是一种以过程为
中心的编程思想。是分析解决问题所需要的步骤,然后用函数把这些步骤一步一步实现并调用就可以了。
8.2 面向过程编程的缺陷
重用性差、可扩展性差、不利于维护
面向对象编程OOP(Object Oriented Programming)是将问题分解成各个对象,建立对象的目的不是
为了完成一个步骤,而是为了描述某个事物在整个解决问题的步骤中的行为。
面向过程的造房子,由一砖一瓦从下往上建造,哪个步骤出了问题很难查出来。
面向对象的造房子,由各个厂家(门,窗户,墙面)生产好,组装成为房子。
8.3 面向对象编程的优点:
已维护,易复用,易扩展,由于面向对象有封装、继承、多态性的特性,可以设计出低耦合的系统,
使系统更加灵活、更加易于维护。
8.4 面向对象三大特性:
(1)封装
将对象的成员变量和实现细节隐藏起来,不允许外部直接访问。例如生产门的厂家对象,将
生产细节隐藏起来,只给你最后的产品:一扇完整的门!
(2)继承
继承是可以让某个类型的对象获得另一个类型的对象的属性的方法。比如生产门的厂家对象,
可能生产阳台门,卧室门,这些门的生产原材料与步骤相同,所以可以直接在某种基本门上直接
加工扩展。
(3)多态
不同类的对象对同一消息作出不同的响应就叫做多态,比如生产门的厂家对生产消息的响应是生产门,
而生窗户的厂家对生产的响应是生产窗户。
8.5 面向对象的分析(OOA, Object Oriented Analysis)
一切事物皆对象,通过面向对象的方式,将现实世界的事物抽象成对象,实现世界中的关系抽象成
类、继承,帮助人们实现对现实世界的抽象与数学建模。通过面向对象的方法,更利于用人理解的方式
对复杂系统进行分析、设计与编程。
用面向对象思想来思考“我吃鱼!”
面向过程思维:
关注吃鱼这件事情:第一步吃鱼头;第二步吃鱼尾
面向对象思维:
关注人与鱼两个对象:
(1)对象:人
属性:姓名等
行为:吃鱼
(2)对象:鱼
属性:种类等
行为:游泳
9. 类与对象
类似抽象,对象是具体
(1)什么是类?
类表示现实生活中的一类事物,比如“学生”!
(2)什么是对象?
对象是一类事物中的一个具体的个体,比如学生“小明”!
书籍类,具体的某本书(红楼梦),对象
“类”其实就是一种类似于结构体的新类型而已!既然是类型,则可以定义变量(类的变量称为对象或实例)!
类名 对象名;
例如:
Studnet stu;
普通说法:定义类的变量stu!
专业说法:定义类的对象stu!
高端说法:类的实例化,对象stu是Student类的一个实例!
class 类名
{
private:
成员变量;
protected:
...
public:
成员函数;
};
//学生类的定义
class Student
{
private:
int num; //学号
char[20] name; //姓名
int age; //年龄
public:
void getAge(); //得到年龄
};
是不是很像结构体?
struct Student
{
int num; //学号
char[20] name; //姓名
int age; //年龄
void getAge(); //得到年龄
};
相似度:90%
类其实就是结构体的进一步进化,尤其是在C++中,结构体升级了,更加接近
(3)类与结构体的区别
类别 默认访问级别 继承访问级别
类 成员变量及成员函数的默认访问级别是private 默认是Public
C++结构体 成员变量及成员函数默认的访问级别是public 默认是private
(4)类的成员
类具有成员属性与成员方法
数据成员:用来描述具体某个对象的特征的是属性,是静态的。也称为成员变量,或成员属性。
一般来讲这些属性设置为私有,仅类内部使用。
例如:作为学生,我有很多属性:姓名,年龄,班级,学号
(5)函数成员
函数成员:每个对象有他们自己的行为或操作,我们把这些行为称之为方法,是动态的。也称为
成员函数(如果前面叫成员变量)或成员方法(如果前面叫成员属性)。一般设置为公有,以方
便外界使用这个类。
例如:作为学生,我有很多行为,写作业,上课,做早操,玩游戏
//定义学生类
class Student
{
private:
int num; //学号
char name[100]; //姓名
int age; //年龄
protected:
int score; //分数
public:
int grade; //年级
void Learn() //学习行为
{
}
void Exam() //考试
{
}
};
#include <iostream>
using namespace std;
//定义学生类
class Student
{
private:
//类的成员变量或成员属性
int num; //学号
char name[100]; //姓名
int age; //年龄
protected:
int score; //分数
public:
int grade; //年级
//类的成员函数或者成员方法
void Learn() //学习行为,在类的内部定义实现,属于内联函数
{
cout << "学习!" << endl;
}
//在类内部仅声明,在类外面实现
void Exam();
};
//前面加上类表示类的成员函数
void Student::Exam() //考试
{
cout << "做作业" << endl;
}
int main()
{
//类的实例化
Student stu; //定义学生类的变量(对象,实例)
return 0;
}
#include <iostream>
using namespace std;
//定义学生类
class Student
{
private:
//类的成员变量或成员属性
int num; //学号
char name[100]; //姓名
int age; //年龄
protected:
int score; //分数
public:
//公有成员
int grade; //年级
//类的成员函数或者成员方法
void Learn() //学习行为,在类的内部定义实现,属于内联函数
{
cout << "学习!" << endl;
}
//在类内部仅声明,在类外面实现
void Exam();
};
//前面加上类表示类的成员函数
void Student::Exam() //考试
{
cout << "做作业" << endl;
}
int main()
{
//类的实例化
Student stu; //定义学生类的变量(对象,实例)
//用"."访问类的公有
stu.grade = 6; //对象初始化
cout << stu.grade << endl;
stu.Exam();
return 0;
}
(6)类成员的访问
对象用"."操作符来访问公有成员变量、成员函数!
例如:Student Stu;
stu.num; //获取学生的学号(假设为公有)
stu.ListenClass(); //听课
10. 类成员的访问权限
private、public、protected控制成员变量、成员函数的权限
(1)类的一个特征就是封装,涉及到类成员能否被外部访问,那么public、protected、private
作用就是实现访问控制。
试想一下家里:
1)邮箱 (内部、外部都可以使用) public
2)房产证 (内部或继承者使用) protected
3)银行卡 (内部使用) private
(2)三种权限的访问区别
访问类型 public(成员变量,成员函数) protected(成员变量,成员函数) private(成员变量,成员函数)
类内部 OK OK OK
对象 OK NG NG
11. 输入输出流cin与cout
(1)替代C中的printf函数与scanf函数最佳选择
在C语言中我们使用printf和scanf两个函数输入输出数据。在C++中我们仍可以使用printf和
scanf,但C++语言提供了一套效率更高的、更方便的输入输出cout和cin。
(2)cin与cout究竟是什么?
cin与cout分别是istream类、ostream类的两个对象!
使用cin、cout需要包含的头文件:
#include <iostream>
using namespace std; //在std名字空间中
(3)格式
cout<<表达式1<<表达式2<<...<<表达式n;
cin>>变量1>>变量2>>...>>变量n;
例:
cin>>a>>b>>c;
cout<<"a="<<a<<"\tb="<<b<<"\tc="<<c<<endl;
12. 函数重载
(1)相同的函数名,不同的参数个数与类型
先思考一个问题,实现三个求和函数:
1)求两个数的和?
2)求三个数的和?
3)求四个数的和?
int sum1(int a, int b)
{
return a + b;
}
int sum2(int a, int b, int c)
{
return a + b + c;
}
int sum3(int a, int b, int c, int d)
{
return a + b + c + d;
}
我们发现一组功能相似的函数,需要用三个不同函数名来描述,程序的可读性很差。
什么是函数重载?
指在同一作用域内,可以有相同函数名,不同参数列表的函数,这组功能相似的函数被称为重载函数。
重载函数减少了函数名的数量,增强了程序的可读性。
int sum(int a, int b)
{
return a + b;
}
int sum(int a, int b, int c)
{
return a + b + c;
}
int sum(int a, int b, int c, int d)
{
return a + b + c + d;
}
(2)为什么需要函数重载?
1)如果没有函数重载机制,为这个 print函数取不同的名字,如print_int、print_string、
print_long型、char *、各种类型的数组等等,这样做很不友好。
2)类的构造函数跟类名相同,也就是说:构造函数都同名。如果没有函数重载机制,则无法实现。
3)操作符重载,本质上就是函数重载,它大大丰富了 已有操作符的含义,方便使用,如+可用于
连接字符串等。
(3)C++如何实现函数重载机制?
C++代码在编译时会根据参数列表对函数进行重命名,例如void Swap(int a, int b)可能会被重命名
为_Swap_int_int(不同的编译器命名方式不同),当发生函数调用时,编译器会根据传入的实参去逐个
匹配,以选择对应的函数,如果匹配失败,编译器就会报错,这就叫重载决议(Overload Resolution)。
(4)函数的重载的规则
1)函数名必须相同。
2)参数列表必须不同(个数不同或者类型不同或者参数排列顺序不同)
3)函数的返回类型可以相同也可以不同
4)仅仅返回类型不同不足以成为函数的重载
类的成员函数的重载也遵循上述规则!
#include <iostream>
using namespace std;
//定义类
class A
{
public:
void print(char a) // 参数类型不同
{
cout << "print(char a)" << endl;
}
void print(int a) //参数类型不同
{
cout << "print(int a)" << endl;
}
void print(float a) //参数类型不同
{
cout << "print(float a)" << endl;
}
void print(int a, int b) //参数个数不同
{
cout << "print(int a, int b)" << endl;
}
};
int main()
{
A a;
a.print('A');
a.print(123);
a.print(1.234f);
a.print(1, 2);
return 0;
输出结果:
print(char a)
print(int a)
print(float a)
print(int a, int b)
13.对象指针
指向类对象的指针
类也是一种类型
类类型 * :就变成类类型指针
类指针变量的赋值自然要指向该类定义的变量(对象)的地址,和基本类型没有什么区别
Student stu;
Student *pStu = &stu;
对比
int stu;
int *pStu = &stu;
对象用“.”操作符来访问公有成员变量、成员函数!
Student stu;
stu.ListenClass(); //用.操作符
类指针用“->”操作符来访问公有成员变量、成员函数
Student stu;
Student *pStu = &stu;
pStu->ListenClass(); //用->操作符
#include <iostream>
using namespace std;
//定义类
class Student
{
public:
int age; //年龄
void ListenClass()
{
cout << "听课!" << endl;
}
};
int main()
{
Student stu; //定义一个类的对象
//对象用.访问公有成员变量与成员函数
stu.age = 18;
stu.ListenClass();
//定义类指针
Student * pStu = &stu; //赋值为类变量(对象)的地址
//对象指针用->访问公有成员变量与成员函数
cout << pStu->age << endl;
pStu->ListenClass();
return 0;
}
14.this指针
一个指向对象本身的指针
每个对象都有一个this指针,指向自己的首地址
如果把某个对象比喻为景区,那么this指针就是山中的路线指示牌,在类中通过this指针,
访问整个对象的信息!
this指针的特点:
1)this指针是自身类型的指针
例如:A a;
那么对象a的this指针 类型为A *,并且this指针的值为a的地址
2)this只能在类成员函数中使用(比如访问自身成员->),在类的外部无法使用。
例如: A a;
那么a.this错误
#include <iostream>
using namespace std;
//定义类
class A
{
public:
void printThis()
{
//this指针只能在类的内部使用,并且this已经被赋值为对象的地址,可放心使用
cout << typeid(this).name() << " "<< this << endl;
}
};
int main()
{
A a; //定义对象a
A *pA = &a;
cout << pA << endl;
a.printThis();
//每一个对象都有一个this指针,指向自己的首地址
A a2;
A *pA2 = &a2;
cout << pA2 << endl;
a2.printThis();
return 0;
}
结果:
0037FD17
class A * 0037FD17
0037FCFF
class A * 0037FCFF
this指针是对象的一部分吗?
this指针并不是类本身的一部分,不会影响sizeof(类)的结果,它由编译器管理,
我们直接使用即可。
this指针是什么时候创建的?
this在成员函数的开始执行前构造,在成员的执行结束后清除。
this指针是怎么传递的?
例如
a.fun(10);
编译器将会编译成:
A::func(&a,10)
从而将this指针赋值
15. C++对C“增强”,表现在两个方面:
1)在原来面向过程的机制基础上,对C语言的功能做了不少扩充。
2)增加了面向对象的机制。
(1)面向对象程序设计,是针对开发较大规模的程序而提出来的,目的是提高软件开发的效率
(2)不要把面向对象和面向过程对立起来,面向对象和面向过程不是矛盾的,而是各有用途,
互为补充。
(3)学习C++,既要利用C++进行面向过程的结构化程序设计,也要会利用C++进行面向对象的程序
设计
(4)C++是由C发展而来的,与C兼容。用C语言写的程序基本上可以不加修改地用于C++。从C++的名字可以
看出它是C的超集。C++既可用于面向过程的结构化程序设计,又可以用于面向对象的程序设计,是一种功能
强大的混合型的程序设计语言。
#include <iostream>
using namespace std; //使用标准的命名空间,除了标准的命名空间还有自己的命名空间
int main()
{
cout << "This is a C++ program." << endl;
return 0;
}
g++ pointer.cpp -o pointer++
输出结果:
This is a C++ program.
#include <iostream>
using namespace std;
class Student
{
private:
int num;
int score;
public:
void setdate()
{
cout << "please input num and score:";
cin >> num;
cin >> score;
}
void display()
{
cout << "num =" << num << endl;
cout << "score=" << score << endl;
}
};
int main()
{
Student stu1, stu2;
stu1.setdate();
stu1.display();
return 0;
}
16.面向过程与面向对象:
面向对象和面向过程的历程:
(1)面向过程编程采取的是时间换取空间的策略,因为在早期配置低、内存小,如何节省内存
则成了首要任务,哪怕是运行时间更长。随着硬件技术的发展,硬件不再成为瓶颈,相反更好的模拟现实世界、
系统的可维护性等问题凸显出来,于是面向对象设计应运而生。
(2)当下:应用在PC机上的一般应用系统,由于不太需要考虑硬件的限制,而系统的维护性等方面却要求
很高,一般采用面向对象方式;而在内存限制有所要求的嵌入式系统,则大多采用面向过程方式进行设计编程。
(3)面向过程:procedure oriented programming pop
面向过程是分析解决问题的步骤,然后用函数把这些步骤一步一步的实现,然后在使用的时候一一调用则可
(4)面向对象 object oriented programming oop
面向对象是把构成问题的事物分解成各个对象,而建立对象的目的也不是为了完成一个一个步骤,而是
为了描述某个事物在解决整个问题的过程中所发生的行为。
17.构造函数
一个做初始化工作的地方
(1)在面向过程编程程序中,如果你要做一些初始化工作,你会怎么做?
你会写一个init函数,并且第一时间调用它!
void init()
{
变量的初始化代码
}
(2)面向对象中,当类也需要做初始化工作,你会怎么做?
写一个init的成员函数,第一时间调用?
构造函数是一种特殊的方法。主要用来创建对象时初始化对象,即为对象成员变量赋初始值等。它会在
对象创建的时候自动被调用!(不需要我们来调用)
构造函数的定义方式一:
class 类名
{
public:
//类内实现构造函数,为内联函数
类名(形参表) //构造函数名就是类名
{
//初始化代码
} //构造函数没有返回值
};
构造函数的定义方式二:
class 类名
{
public:
//类内只声明
类名(形参表);
}
//类外构造函数的实现
类名::类名(形参表)
{
//初始化代码
}
和类的成员函数定义方式类似。类的成员函数定义也有两种方式,第一种是在类里面定义并实现
第二种是只在类里面声明,类外面实现。但是类外面实现的函数名前面要加上类名。不同点是
构造函数的函数就是类名,并且没有返回值。
(3)构造函数的特点:
1)构造函数名与类名相同,无返回值。
2)它是类的成员函数,以直接访问类的所有数据成员。
3)在类的内部定义的构造函数是内联函数。
4)类如果没有定义类的构造函数,编译系统就会自动生成一个默认无参的构造函数。
一旦开发者编写构造函数,系统不再提供构造函数!
5)默认构造函数(不带参或带默认形参值)也可以由开发人员自己编写
18.explicit关键字
explicit中文意思是明确的,清楚的
#include <iostream>
using namespace std;
class A
{
public:
//自定义的有参构造函数
A(int a)
{
this->a = a; //因为是在类内部,可以访问private成员变量
}
void print()
{
cout << a << endl;
}
private:
int a;
};
int main()
{
//标准的写法,调用有参构造函数
A a(111);
a.print();
return 0;
}
19. 类型增强
(1)类型检查更严格
比如,把一个const类型的指针赋给非const类型的指针。C语言中可以通过,但是在
C++中则编不过去。
在C++中
#include <stdio.h>
int main(void)
{
const int a = 10;
int *p = &a; //“const int *”类型的值不能用于初始化“int *”
return 0;
}
int main(void)
{
const int a = 10;
const int *p = &a; //左右两侧都为“const int *”类型,编译通过
return 0;
}
int main(void)
{
const int a; //常量变量不赋初值通不过,现在不给他初始化以后没机会初始化了
const int *p = &a; //这种提示很好
return 0;
}
#include <iostream>
using namespace std;
int main(void)
{
char *p = malloc(100); //malloc()返回的是void *类型的内存,与char *不匹配,C++ 的编译器不让过
return 0;
}
#include <iostream>
using namespace std;
int main(void)
{
char *p = (char *)malloc(100); //先把void *转成char *,C++编译器就可以通过了
//C++类型检查更加严格
return 0;
}
在C中可以编译通过,不太严谨
#include <stdio.h>
int main(void)
{
const int a = 10;
int *p = &a; //这里C语言允许把const int *类型赋值给了int *类型,说明C语言的类型检查不严格,不严谨说明比较灵活
*p = 100; //明的不能改,我暗的把你改了
printf("*p = %d.\n", *p); //100
printf("a = %d.\n", a); //100
return 0;
}
#include <stdio.h>
int main(void)
{
const int a;
int *p = &a;
printf("*p = %d.\n", *p); //不赋初值编译通过,只不过是随机值而已
printf("a = %d.\n", a);
return 0;
}
#include <stdio.h>
int main(void)
{
char *p = malloc(100); //malloc()返回的是void *类型的内存,与char *不匹配,但是C语言编译器可以通过
return 0;
}
19. 增加新的数据类型
(1)String 字符串类型,C语言是没有字符串类型的
(2)bool 布尔类型 值取true和false
C语言是没有bool类型的,C语言中表示真假,用0和非0
C++同样遵循用0表示false,用非零表示true,感觉这里C++做的不是太好,应该只能取true和false两个值最好
C++引入bool型,其实bool类型的实现方式是个枚举
#include <iostream>
using namespace std;
enum BOOL
{
FALSE,TRUE
};
int main()
{
BOOL b = FALSE;
if(b)
{
printf("b value is false\n");
}
else
{
printf("b value is true\n");
}
return 0;
}
bool类型占用1个字节
(3)真正的枚举
C++中枚举类型定义的变量只能从列举的成员中选,C语言可以从成员外选
#include <iostream>
using namespace std;
enum DAY
{
Mon, Tue, Wen
};
int main()
{
enum DAY today = 10; //C++报错,C++收紧了
return 0;
}
C语言中的枚举:
enum DAY
{
Mon, Tue, Wen
};
int main()
{
enum DAY today = 10; //完全没问题,更灵活,更开放
return 0;
}
其实放开和收紧都是对的,没有好与不好,只是说收的越紧越好用
20.枚举
枚举类型,指一个被命名的整型常数的集合。本质上是一组常数的集合体,只是这些常数有各自
命名。由于枚举变量的赋值,一次只能存放枚举结构中的某个常数。所以枚举变量的大小,实质
是常数所占内存空间的大小(常数为int类型,当前主流的编译器中一般是32位机器和64位机器
int型都是4个字节)。枚举类型所占用内存大小也是这样。
C语言中枚举本质就是整型,枚举变量可以用任意整型赋值。而C++中枚举变量,只能
用被枚举出来的元素初始化
枚举变量究竟是多大呢?答案是4个字节,为啥?
枚举里面的成员是常量,枚举变量的取值为花括号内任意一个值(有且只能有其中一个值),而这个值
是int型的,在X86系统中,int型的数据占内存4个字节,所以枚举变量的值为4个字节。
enum SEASON
{
Spr, Sum, Autu, Win
};
int main()
{
SEASON s; //可以省略enum
s = Sum; //只能取列举出来的成员,
return 0;
}
enum SEASON
{
Spr, Sum, Autu, Win
};
int main()
{
SEASON s;
s = 100; // error: invalid conversion from `int' to `SEASON'
return 0;
}
(1)C++中的枚举才是真正意义上的枚举,只能从列举成员中选取
下面这种枚举的写法与宏是等价的
#define Spr 0
#define Sum 1
#define Autu 2
#define Win 3
与
enum SEASON
{
Spr, Sum, Autu, Win
};
等价
(2)能用枚举代替的就不用宏
补充知识点:表达式
在C语言中:
#include <stdio.h>
int main()
{
int a, b = 10;
a = b = 100; //b = 100, a = (b = 100) 表达式可以给别人赋值
printf("a = %d b = %d\n", a, b);
return 0;
}
#include <stdio.h>
int main()
{
int a, b = 10;
(a = b) = 100; //报错,C语言表达式是不可以被赋值的,即表达式不可以做左值
printf("a = %d b = %d\n", a, b);
return 0;
}
在C++中:
#include <stdio.h>
int main()
{
int a, b = 10;
(a = b) = 100; //通过结果为a = 100, b = 10,表达式(a = b)的值等于a, 100给a赋值
//等价为b = 10 把b的内容赋给了a ,再把100赋给了a
printf("a = %d b = %d\n", a, b);
return 0;
}
#include <stdio.h>
int main()
{
int a, b = 10;
(a = b) = 100;
printf("a = %d b = %d\n", a, b);
(a != b ? a : b) = 1000; //三目运算符,先判断条件a != b,为真则计算a的值,为假则计算b的值
printf("a = %d b = %d\n", a, b);
return 0;
}
21. cin cout
cin 和cout是C++的标准输入流和输出流,他们在头文件iostream中定义
//cin cout 是类的对象 scanf printf是函数,两者具有相同的功能
#include <iostream>
using namespace std;
int main()
{
char name[30];
scanf("%s",name); //这样做输入超过30个字符后,回车程序会挂掉
return 0;
}
#include <iostream>
using namespace std;
int main()
{
char name[30];
// scanf("%s",name);
cin >> name; //表示键盘中的数据流入name中去了
//符号>>在C中叫右移运算符,C++中叫流输入运算符
//这种就是一词多义,C++中我们叫重载,就像china有瓷器和中国的意思,看语境
//这样做输入超过30个字符后,回车程序会挂掉
printf("name = %s\n", name);
return 0;
}
#include <iostream>
using namespace std;
int main()
{
char name[30];
// scanf("%s",name);
cin >> name;
// printf("name = %s\n", name);
cout << " name = " << name << endl; //先把" name = "流到cout中去,再把name流到cout中去
//再把endl流到cout中去,写在前面的先流
return 0;
}
与下面的写法完全等价
#include <iostream>
using namespace std;
int main()
{
char name[30];
// scanf("%s",name);
cin >> name;
// printf("name = %s\n", name);
cout << "name =";
cout << name;
cout << endl;
return 0;
}
#include <iostream>
using namespace std;
int main()
{
char name[30];
// scanf("%s",name);
// cin >> name;
gets(name); //这个函数也不安全,也不关心你是否溢出,输入超过30个字符后也会挂掉
// printf("name = %s\n", name);
cout << "name =";
cout << name;
cout << endl;
return 0;
}
C语言中没有安全的东西,它所谓的安全就是我把安全交给你,C语言是一门相当自由奔放的语言
#include <iostream>
using namespace std;
int main()
{
char name[30];
// scanf("%s",name);
// cin >> name;
fgets(name, 30, stdin); //这里就限制读取29个字符,如果你把限制往上提,那么一样不安全
//这就是C语言把安全交给你的体现
// printf("name = %s\n", name);
cout << "name =";
cout << name;
cout << endl;
return 0;
}
那C++中有没有安全的呢?有
答案就是String
需要注意的是String是不能用Printf()来输出的
#include <iostream>
using namespace std;
int main()
{
// char name[30];
// scanf("%s",name);
// cin >> name;
// fgets(name, 30, stdin);
// printf("name = %s\n", name);
string name; //那这个到底能输入多少个字符
cin>>name;
cout<<name.max_size()<<endl; //可以输入1073741820个字符
//endl等价于C语言中\n 换行
cout << "name =";
cout << name;
cout << endl;
return 0;
}
所以在C++中所有用字符数组来解决的问题全部用字符串来解决,这个非常安全,好用
#include <iostream>
using namespace std;
int main()
{
int a, b;
cin >> a >> b; //等价于cin >> a; cin >> b; 里面只决定了先后顺序,通过空格/TAB/区分/回车输入值
//回车也可表示接收
cout << a << b << endl;
return 0;
}
以前C语言中是怎么处理的
#include <stdio.h>
int main()
{
int a;
char b;
scanf("%d%c", &a, &b); //这里空格被吃掉了
printf("a = %d, b = %c\n", a, b);
return 0;
}
#include <iostream>
using namespace std;
int main()
{
int a;
char b;
cin >> a >> b; //等价于cin >> a; cin >> b;
cout << a << b << endl;
printf("a = %d, b = %c\n", a ,b); //C++中已经解决吃空格的问题了
return 0; //看来C++中把C语言的一些细节优化得很舒服了
}
C语言中printf拥有强大的格式化控制,C++亦可以实现,略复杂。
用cout输出不用关心%d等格式问题,是什么就输出什么
#include <iostream>
#include <iomanip> //C++的头文件有个特点,没有.h
using namespace std;
int main()
{
int a = 12345;
char b = 1234.567;
cout << "xxxxxxxx" << endl;
cout << setw(8) << a << endl; //占8个位置,不够就在前面补空格,这种情况是右对齐
return 0;
}
#include <iostream>
#include <iomanip>
using namespace std;
int main()
{
int a = 12345;
char b = 1234.567;
cout << "xxxxxxxx" << endl;
cout << setiosflags(ios::left) << setw(8) << a << endl; //左对齐
return 0;
}
#include <iomanip>
using namespace std;
int main()
{
int a = 12345;
char b = 1234.567;
cout << a << endl; //默认输出十进制
cout << hex << a << endl; //十六进制
cout << oct << a << endl; //八进制
cout << dec << a << endl; //十进制
//与下面等价
printf("%x\n", a);
printf("%o\n", a);
printf("%d\n", a);
return 0;
}
时钟显示
#include <iostream>
#include <iomanip>
using namespace std;
int main()
{
int hour = 0, min = 11, sec = 0;
cout << setfill('0') << setw(2) << hour << ":" << setw(2) << min << ":" << setw(2) << sec << endl;
return 0;
}
22. 函数重载(function overload)
什么是重载?
同一个东西表示了不同的意思。比如china可以表示一个国家,也可以表示瓷器,不同的意思由不同
的语境决定,这种现象就叫重载。
函数重载构成的要求:
(1)函数名相同
(2)函数参数列表不同,类型,个数,顺序
#include <iostream>
using namespace std;
//函数名相同,函数参数列表不同(类型,个数,顺序)
void print(int a) //参数类型不同
{
}
void print(char a) //参数类型不同
{
}
void print(int a, char b) //参数个数不同
{
}
void print(char a, int b) //顺序不同
{
}
int main()
{
return 0;
}
C语言中:
#include <stdio.h>
int main()
{
return 0;
}
int func(int a)
{
}
int func(char a)
{
}
// error: conflicting types for 'func'
C++中:
#include <iostream>
#include <iomanip>
using namespace std;
int main()
{
return 0;
}
int func(int a)
{
}
int func(char a)
{
}
//编译通过
#include <iostream>
using namespace std;
int func(int a);
int func(char a);
int main()
{
int a;
func(a);
char b;
func(b);
return 0;
}
int func(int a) //C++中可以有相同的函数名,相同的函数名可以表达不同的意思,
{
printf("int func(int a)\n"); //不同的意思由语境决定,这里这个语境就是形参
}
int func(char a)
{
printf("int func(char a)\n");
}
来看一下不重载的情况
#include <iostream>
using namespace std;
int iabs(int data);
float fabs(float data);
int main()
{
int ret = iabs(-5);
cout << ret << endl;
return 0;
}
int iabs(int data)
{
return data>0? data:-data;
}
float fabs(float data)
{
return data>0? data:-data;
}
//不同类型的绝对值函数都要定义一遍的话看起来非常繁琐,毕竟里面的功能是相同的
#include <iostream>
using namespace std;
int abs(int data);
float abs(float data);
int main()
{
float ret = abs(-5.5f); //根据参数自动选择进入对应函数
cout << ret << endl;
return 0;
}
int abs(int data)
{
return data>0? data:-data;
}
float abs(float data)
{
return data>0? data:-data;
}
#include <iostream>
using namespace std;
//函数名相同,函数参数列表不同(类型,个数,顺序)
void print(int a) //参数类型不同
{
cout << "void print(int a)" << endl;
}
void print(char a) //参数类型不同
{
cout << "void print(char a)" << endl;
}
void print(int a, char b) //参数个数不同
{
cout << "void print(int a, char b)" << endl;
}
void print(char a, int b) //顺序不同
{
cout << "void print(char a, int b)" << endl;
}
int main()
{
int a = 0;
char b = '0';
print(a);
print(b);
print(a, b);
print(b, a);
return 0;
}