C++笔记

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;
}

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值