c++ primer plus学习笔记

预备知识

c++在c语言的基础上添加了面向对象编程和泛型编程

c语言诞生的背景:贝尔实验室开发UNIX操作系统,需要一种高级语言代替汇编语言,避免在每台计算机上执行不同的汇编程序

c语言编程的原理(强调的是算法)

  1. 需要处理的概念——数据和算法
  2. 结构化编程和自顶向下的编程方法,将程序不断划分成程序单元,便于阅读和管理

面向对象编程:

  1. 面向对象编程强调数据,用类描述特定的数据结构
  2. 首先设计类,然后再设计程序解决问题,从低级组织到高级组织的处理过程叫做自下向上的编程

面向对象编程的优点:
1. 有助于创建可重用的代码
2. 信息隐藏可以保护数据
3. 多态能够为运算符和函数创建多个定义,通过编程上下文确定使用哪个定义
4. 继承能够使用旧类派生出新类

泛型编程:

  1. 目标:使重用代码和抽象通用概念的技术更简单
  2. 强调独立于特定数据类型
  3. 提供了执行常见任务的工具,即泛型编程与面向对象编程的应用场景不同
  4. 泛型指的是创建独立于类型的代码,比如设计一个排序函数,此排序函数不单独为整形、浮点型所设计,而泛型编程对语言进行扩展,以便可以只编写一个泛型函数

可移植性

在不修改代码的情况下,更换计算机平台,重新编译程序后,程序运行良好,则该程序是可移植性的

可移植性的挑战:
1. 硬件——避免
2. 语言——标准:异常、运行阶段类型识别、模板和便准模板库
3.

编译和链接

使用visual c++2015进行编程
步骤:
1. 新建项目
2. 选择win32–win32 console application
3. 选择空项目

IDE环境
1. compile对当前打开的文件中的代码编译
2. buildmake编译项目中所有源代码文件的代码,重新编译新修改文件
3. build all重新编译所有源代码文件
4. 为了查看程序输出,可以在程序的最后加上下面的代码

cin.get();
cin.get();
return 0;

cin.get()语句读取下一次键击,上述语句让程序等待,直到按下Enter键。如果程序在常规输入后留下一个没有被处理的键击,那么第二条语句是必需的,enter键将被第一个cin.get()吸收

问题

问题1:什么是泛型编程?
对编程语言进行扩展,以使程序不针对特定数据类型而适用

问题2:什么是多态和编程上下文?

2018/7/14


开始学习c++

一个简单的c++程序:

#include<iostream>

using namespace std;

int main()

{
    for (int i = 0; i < 5; i++) {
        cout << i << endl;
    }

    cout << "hello world" << endl;

    system("pause");

    return 0;

}

main()函数

int main()叫函数头,{}包含的部分叫函数体

  1. 作为接口的函数头

函数头描述了函数与调用它的函数之间的接口,函数名前面的部分叫做函数返回类型,函数名后括号中的而部分叫做形参列表。
通常main()被启动代码调用,启动代码是由编译器添加到程序中的,是程序和编译系统之间的桥梁

如果编译器到达main()函数末尾时没有遇到返回语句,认为以return 0结尾

c语言中,省略返回类型相当于函数类型为int,但是c++淘汰了这种用法

注释

c++注释://

c注释:/**/

c++预处理器和iostream文件

如果需要使用c++的输入或者输出工具,需要提供下面两行代码:

#include <iostream>
using namespace std;

该编译指令使预处理器将iostream文件的内容添加到程序中,这涉及程序与外部世界之间的通信,iostream文件中的内容,将取代第一行代码,因此使用cincout的程序必须包含iostream

c头文件的扩展名为h,c++头文件没有扩展名

名称空间

名称空间编译指令using namespace std;

名称空间所解决的问题:名称空间支持是c++一项特性,在引用他人代码组合起来的时候解决函数名称共用问题。所引用的代码封装在名称空间单元中,可以用名称空间的名称来指出用哪个厂商的产品,比如wanda()函数的全称为Microflop::wanda() Piscine::wanda(),这样程序可以根据名称空间区分不同版本

所以cout实际上是std::cout,endl实际上是std::endl

所以也可以使用下面的代码代替using namespace std;

using std::cout;
using std::endl;
using std::cin;

使用cout进行c++输出

cout是一个预定义的对象,<<表示信息流动方向,实际上是运算符重载

问题:什么是运算符重载?
允许用户定义的类型重新定义运算符的含义

  1. endl:重起一行,位于名称空间std中,在iostream中定义的,保证程序继续运行前刷新输出
  2. \n:count << "what are you\n";
    引号扩起的字符串通常用 \n,其他情况用 endl
int main()

{
    int carrots;
    carrots = 25;
    cout << "I have ";
    cout << carrots;
    cout << " carrots." << endl;
    carrots = carrots - 1;
    cout << "Now i hava " << carrots << " carrots" << endl;
    system("pause");
    return 0;

}
在声明变量的时候将为变量分配内存空间 赋值语句将值赋给存储单元

可以连续使用赋值运算符

“`cout“`在打印之前,必须将整数形式的数字转换为字符串形式,“`

类描述的是数据格式和用法,对象则是根据数据格式创建的实体 类描述指定了可对类对象执行的所有操作,c++两种发送消息的方式:类方法和运算符重载

c++中,函数调用必须 包括括号,即使没有参数

用户定义的函数

添加另一个用户定义的函数,在使用前必须提供原型,放到main()定义之前
  1. 没有返回值的函数
#include<iostream>

using namespace std;

void simon(int);

int main()

{
    simon(3);
    system("pause");
    return 0;

}

void simon(int n)
{
    using namespace std;
    cout << "simon says touch your toes " << n << " times." << endl;
}

定义simon()的源代码位于main()后面,不允许将函数定义嵌套在另一个函数定义中

  1. 有返回值的函数
#include<iostream>

using namespace std;

int stonetolb(int);

int main()

{
    int pounds = stonetolb(2);
    cout << pounds << endl;
    system("pause");
    return 0;

}

int stonetolb(int sts)
{
    return 14 * sts;
}

处理数据

内置的c++数据类型分为——
基本类型:整数和浮点数
复合类型:数组、字符串、指针和结构

c++命名规则其一:以两个下划线或下划线和大写字母大头的名称被保留给实现,以一个下划线开头的名称被保留给实现,用作全局标识符

基本类型

  1. 整型short\int\long\long long
    • short至少16位
    • int至少和short一样长
    • long至少32位,且至少与int一样长
    • long long至少64位,且至少与long一样长

可以用sizeof运算符返回类型或者变量的长度,单位是字节

#include<iostream>

using namespace std;

int main()

{
    int a = INT_MAX;
    long n_long = LONG_MAX;
    long long x_llong = LLONG_MAX;
    cout << "short is " << sizeof (short) << " bytes." << endl;//2
    cout << "int is " << sizeof(int) << " bytes." << endl;//4
    cout << "long is " << sizeof(long) << " bytes." << endl;//4
    cout << "long long is " << sizeof(long long) << " bytes." << endl;//8
    cout << "int is " << a << " bytes." << endl;// 2147483647
    cout << "n_long is " << n_long << " bytes." << endl;// 2147483647
    cout << "x_llong is " << x_llong << " bytes." << endl;// 9223372036854775807
    system("pause");
    return 0;

}

初始化的方式:

    int emus{ 7 };
    int rheads = { 12 };

无符号类型

unsigned short change
#include<iostream>

using namespace std;

int main()

{
    int n_int = INT_MAX;
    unsigned int un_int = INT_MAX;
    cout << n_int << endl;
    cout << un_int << endl;
    cout << n_int + 1 << endl;
    cout << un_int + 1 << endl;
    system("pause");
    return 0;

}

无符号整型可以增大变量能够存储的最大值
上面的例子中n_int进行过加1操作会变成赋值,因为符号为产生进位

整形字面量

c++能够用三种不同的计数方式来书写整数:基数为10,8,16
如果第一位是0,第二位是1-7,则基数是8;
如果前两位是0x或0X,则基数是16.

在默认情况下,cout以十进制格式显示整数,在计算机中存储都以二进制形式进行存储

endl提供了控制符dec“`hex\oct分别指示cout“`以十进制、十六进制和八进制格式显示整数
在打印的下一行加入下面的代码

cout << hex;

char类型
char类型是另一种整型,能存储所有基本符号

#include<iostream>

using namespace std;

int main()

{
    char ch;
    cin >> ch;
    cout << ch << endl;
    system("pause");
    return 0;

}

程序输入字母也被输出,但是内存中存储的内容为字母对应的字符编码
实际上,程序调用了coutput函数
cout.put(ch)
实际上ch存储的是整数,可以执行整数的操作,如加法

#include<iostream>

using namespace std;

int main()

{
    char ch;
    cin >> ch;
    cout << ch << endl;
    cout << ch + 1 << endl;
    system("pause");
    return 0;

}

最后一行将输出78
如果输入数字5,ch中将存储5的字符编码53,所以第二行输出54

#include<iostream>

using namespace std;

int main()

{
    char ch;
    cin >> ch;
    cout << ch << endl;
    cout << ch + 1 << endl;
    cout.put(ch);
    system("pause");
    return 0;

}

书写字符常量的最简单方法是用单引号括起

'A'
'5'

如果char型存储的是数值类型,那么unsigned charsigned char差异很重要,signed char表示范围是-128–127

布尔类型

非零值为true,零为false,true 可以转换为1,false转换为0

const限定符

const关键字修改变量声明和初始化

const int Months = 12;

将名称的首字母大写,提醒自己表示常量

浮点数

float
double
long double

浮点数表示法:

12.3
2.52e+8//小数点向右移动8位
8.33E-4//小数点向左移动4位

c++算数运算符

+
-
*
/
%

除法分支,如果两个操作数都是整数,执行整数除法,小数部分被丢弃,有一个是浮点数,则结果为浮点数

类型转换

将一个值赋值给取值范围更大的类型通常不会导致问题,但是将一个很大的值赋值给一个较小的值会降低精度。

int('Q');

c++11中的auto声明,让编译器能够根据初始值的类型推断变量的类型

复合类型

数组

编译器不会检查使用的下标是否有效

数组初始化:int a[3] = {20,1,2};

初始化时,提供的值可以少于数组的元素数目,如果只对数组一部分进行初始化,编译器将把其他元素设置成0。初始化全为0:long b[3] = {0};

short thing[] = {1,5,2,3};编译器会计算元素个数

拓展:vector

字符串

用char数组类型存储字符串,需要以空字符结尾,空字符被写作\0

char dog[3] = {'b','e','a'};//not a string
char cat[3] = {'b','e','\0'};//a string

很多处理字符串的函数的处理规则都是以遇到空字符停止

下面的字符串初始化也是合理的,用引号括起的字符串隐式包括 结尾的空字符

char bird[11] = "Mr. cheeps";
char fish[] = "bubbles";

在确定存储字符串所需的最短数组时,记得将结尾的空字符计算在内

拓展:string

标准头文件cstring提供了很多与字符串相关的函数

#include<iostream>
//#include<cstring>
using namespace std;

int main()

{
    const int Size = 15;
    char name1[Size];
    cin >> name1;
    cout << strlen(name1) << endl;
    system("pause");
    return 0;

}

但是尝试注释调cstring头文件也没有报错

strlen不会计算空字符,若数组存储内容中间含有空字符,即使后面还有字符,字符串也会在空字符处结束;cin使用空白(空格、制表符和换行符)来确定字符串的结束位置

解决各个单词空格分开的问题:
每次读取一行字符串输入
istream中的类提供了一些面向行的类成员函数:getline()get(),读取一行输入,直到到达换行符,但是getline()会丢弃换行符,get()将换行符保留在输入序列中。

getline()

调用方法:

cin.getline(数组名称,字符数)

若字符数为20,最多读取19个字符

    const int Size = 15;
    char name1[Size];
    char name2[Size];
    cin.getline(name1, Size);
    cin.getline(name2, Size);
    cout << name1 << endl;
    cout << name2 << endl;

输入

you
are

输出:

you
are
get()

调用方法:

cin.get(数组名称,字符数)
    const int Size = 15;
    char name1[Size];
    char name2[Size];
    cin.get(name1, Size);
    cin.get(name2, Size);
    cout << name1 << endl;
    cout << name2 << endl;

输入:

you[回车]

自动输出(回车仍然在队列中):

you
[空行]

下面的代码等同于上上面的代码,用cin.get()吸收回车

    const int Size = 15;
    char name1[Size];
    char name2[Size];
    cin.get(name1, Size);
    cin.get();
    cin.get(name2, Size);
    cout << name1 << endl;
    cout << name2 << endl;

下面代码等同:

    cin.get(name1, Size).get();
    cin.get(name2, Size);

get() 方法相比于getline()方法有优势,假设用get()将一行读入数组,如何知道停止读取的原因是由于读取了整行,而不是由于数组填满?解决方法就是查看下一个输入字符,如果是换行符说明读了整行,否则说明还有其他输入

还会有空行和其他问题,如何解决?

c++常使用指针而不是数组来处理字符串

string类简介

string类包含在头文件string中,string类位于名称空间std中。string类定义隐藏了字符串的数组性质

未被初始化的数组,第一个空字符的出现位置是随机的,string类型未被初始化的长度为0

#include<iostream>
#include<string>
using namespace std;

int main()

{
    string str1;
    string str2 = "pather";
    cin >> str1;
    cout << str1 << endl;
    cout << str2 << endl;
    cout << str2[2] << endl;
    system("pause");
    return 0;

}

类设计能够让程序自动处理string的大小

初始化:

string a = {"you are my baby"};

赋值、拼接、附加

  1. 不能将一个数组赋值给另一个数组,但是可以将一个string对象赋给另一个string对象
string str1;
string str2 = "father";
str1 = str2;
  1. 可以使用+将两个string对象合并起来,还可以用运算符+=将字符串附加到string对象末尾
string str3;
str3 = str1 + str2;
str1 += str2;
  1. 其他操作
    • strcpy(charr1,charr2);//copy charr2 to charr1
    • strcat(charr1,charr2);//append contents of charr2 to charr1
    • str1.size()
    • strlen(charr1)
    • -

下面代码的cin作为参数传入,意在说明到哪里去找str1,此时这里的getline方法不是一个类方法,友元函数了解一下

    string str1;
    getline(cin, str1);
    cout << str1 << endl;

问题:什么是友元函数?

结构简介

struct inflatable[名称]{
  char name[20];
  float volume;
  double price;
};

一个结构体例子

#include<iostream>
#include<string>
using namespace std;
struct MyStruct
{
    char name[10];
    int age;
    bool sex;
};

int main()

{
    MyStruct guest = 
    {
        "Arya",
        18,
        true
    };
    cout << guest.name << guest.age << guest.sex << endl;
    system("pause");
    return 0;
}

结构声明的位置很重要,放在main()外面是外部声明,可以被后面的任何函数使用,内部声明只能被该声明所属的函数通用。

Mystruct person[100];
    MyStruct guests[2] =
    {
        {"name1",12,true},
        {"name2",20,false}
    };

共用体

用于节省内存,细节再看吧

枚举

c++的enum工具提供了另一种创建符号向量的方式,可以代替const

enum spectrum {red,orange,yellow};
  • spectrum成为新类型的名称
  • 让red,orange,yellow作为符号常量,对应整数值0-2
  • 可以使用枚举名声明这种类型的变量spectrum band
    枚举只定义了赋值运算符,没有定义运算

设置枚举量的值

  • 可以使用赋值运算符显式设置枚举量的值
enum bits{one = 1,two = 2, four = 4,eigth = 8};
  • 可以只显示定义其中的一些枚举量的值
enum bits{one,second = 1000, a};

枚举量具有取值范围

指针和自由存储空间

指针是一个变量,存储的是值的地址,而不是值本身,如果要找到常规变量的地址,只需要对变量应用地址运算符&,就可以获得

    int a = 4;
    cout << &a << endl;

显示地址时,显示十六进制表示法,常用于描述内存的表示法

指针用途存储值得地址,因此指针名表示得是地址,*运算符被成为间接值或解除引用运算符,应用于指针,可以得到该地址存储得值,

    int a = 4;
    int * a_address;
    a_address = &a;
    cout << "a address: " << &a << endl;
    cout << "a_update address: " << a_address << endl;
    cout << "a value: " << *a_address << endl;
    a += 1;
    cout << "a_update address: " << &a << endl;
    cout << "a address: " << a_address << endl;
    cout << "a_update value: " << *a_address << endl;

以上程序结果表明,指针变量初始化后便不会再更改,当a得值变化时,可以根据*a_address访问到该地址存储得数值

下面的声明创建一个指针和一个变量

int* p1,p2;

下面的声明是有效的:

double * tax_ptr;
char * str;

可以在声明语句的时候初始化指针,在这种情况下,被初始化的是指针,而不是它指向的值,创建指针的时候,计算机将分配用来存储地址的内存,但不会分配用来存储指针所指向的数据的内存

使用new来分配内存

前面都将指针初始化为一个变量的地址,但是指针真正的用途是在运行阶段分配未命名的内存以存储值,这种情况下只能通过指针来访问内存,c语言可以用库函数malloc()分配内存,但c++更好的办法是new运算符

    int * pn = new int;
    cout << "new de address: " << pn << endl;

new int告诉程序,需要存储int的内存,找到这块内存把地址返回给pn。

为一个数据对象获得并指定分配内存的通用格式如下:

typeName * pointer_name = new typeName;

使用delete释放内存

使用delete时,后面要加上指向内存块的指针

int * ps = new int;
delete ps;

释放ps指向的内存,但不会删除指针ps本身,一定要配对的使用new和delete,否则将发生内存泄漏,被分配的内存再也无法使用

  • [x] 不要用delete释放声明变量所获得的内存,下述代码将不被允许
int jugs = 5;
int * pi = &jugs;
delete pi;

使用new创建动态数组

int * psome = new int [10];

new运算符返回第一个元素的地址,赋值给psome,并使用完内存块后,应用下面的格式释放:

delete [] psome;

[]表示释放整个数组

问题:如果使用new []为一个实体分配内存,则应该使用delete来释放?

利用指针访问数组中的元素:

    int * pn = new int [3];
    pn[0] = 1;
    pn[1] = 3;
    pn[2] = 5;
    cout << "array" << pn[0] << pn[1] << pn[2] << endl;
    delete [] pn;

可以把指针名当成数组名

指针、数组和指针算术

指针变量加1后,增加的量等于它指向的类型的字节数

    double w[3] = { 10000.0,20000.0,30000.0 };
    double * pw = w;
    cout << "pw = " << pw << ", *pw= " << *pw << endl;
    pw = pw + 1;
    cout << "pw = " << pw << ", *pw= " << *pw << endl;

输出:

pw = 006FF7C4, *pw= 10000
pw = 006FF7CC, *pw= 20000

指针和字符串,没有仔细看,用到的时候再看

使用new创建动态结构

Mystruct * ps = new Mystruct;

创建动态结构时,不能将成员运算符句点用于结构名,因为这种结构没有名称,只是知道他的地址,c++专门为这种情况提供了一个运算符->,用于指向结构的指针

如果结构标识符是结构名,则使用句点运算符,如果标识符是指向结构的指针,则使用箭头运算符

#include<iostream>
using namespace std;
struct MyStruct
{
    char name[10];
    float volume;
    double price;
};

int main()

{
    MyStruct * ps = new MyStruct;
    cin.getline(ps->name, 20);
    cin >> ps->price;
    cin >> (*ps).volume;
    cout << ps->name << ps->price << ps->volume << endl;
    system("pause");
    return 0;
}

自动存储、静态存储和动态存储(线程存储)

自动变量通常存储在栈中,执行代码块时,其中的变量依次加入到栈中,离开代码块时,将按照相反的顺序释放这些变量。
静态存储是整个程序执行期间都存在的存储方式,使变量成为静态的方式有两种:一种在函数外面定义它,一种实在声明变量的时候使用关键字static

自动存储和静态存储的关键在于这些方法限制了变量的寿命,静态变量可能存在于程序的整个生命周期,自动变量只是在特定函数被执行时存在

动态存储管理了一个内存池,静态变量和自动变量的内存是分开的,数据的生命周期不完全受程序或者函数的生存时间控制。

数组的替代品vector和array

#include<iostream>
#include<vector>
using namespace std;

int main()

{
    vector<int> vi;
    int n;
    cin >> n;
    vector<double> vd(n);
    system("pause");
    return 0;
}

vector可以动态增删元素
vector比数组强大,但是比数组效率低,如果是长度固定的数组,则数组更佳
array对象的长度也是固定的,使用栈而不是自由存储区,因此效率与数组相同。
array对象允许直接复制,而数组必须逐元素复制

#include<iostream>
#include<array>
using namespace std;

int main()

{
    array<int,5> vi;
    array<double, 4> vd = {1.2,2.2,1.2,3.4};
    system("pause");
    return 0;
}

可能导致数组越界行为,可以使用成员函数at()可以在运行期间捕获非法索引,程序默认终端,或者利用begin()end()确定边界

循环和关系表达式

++x
x++

while(guests++ < 10)
    cout << guests << endl;

先判断guests是否小于10,然后将guests++,再执行循环体

strcmp("word","word");

比较两个字符串是否相同

    int n = 1;
    do
    {
        n++;
    } while (n <= 7);

满足条件的时候执行循环体

    double prices[5] = { 1.2,1.3,1.4,3.5,2.4 };
    for (double x : prices)
        cout << x << endl;

遍历数组中的元素

    double prices[5] = { 1.2,1.3,1.4,3.5,2.4 };
    for (double x : prices)
    {
        x = x + 1;
        cout << x << endl;
    }
    cout << prices[3] << endl;
    for (double &x : prices)
    {
        x = x + 1;
    }
    cout << prices[3] << endl;

第一种循环不改变数组中的值,第二种引用变量可以使数组中的值改变

分支语句和逻辑运算符

字符函数库cctype,==貌似有问题,测试了几个案例都木有通过p194==

c++从c语言继承了一个与字符相关的,非常方便的函数软件包,可以简化诸如确定字符是否为大写字母、数字、标点符号等工作

#include<cctype>
函数名称 返回值 案例 备注
isalnum(字母或数字) if 数字,0 if字母2 isalnum(33)
isalpha(字母) if 字母,true
iscntrl() if控制字符,true
isdigit() if数字,true

switch语句

    int a = 1;
    switch (a)
    {
    case 1: cout << a << endl;
        break;
    case 2: cout <<"a: " << a + 1 << endl;
        break;
    default:
        cout << "break\n";
        break;
    }

从上到下匹配条件,直到条件合适,从匹配到的条件一下的case语句的执行语句也都执行,所以要每条都加break

break和continue语句

continue语句用于循环中,让程序跳过循环体中余下的代码,并开始新一轮循环;在switch语句或任何循环中使用break语句,使程序跳到switch或循环后面的语句处执行

简单文件输入/输出

  • 写入到文本文件中
    文件输出条件:
    1. 必须包含头文件iostream
    2. 头文件iostream定义了一个用于处理输出的ostream类
    3. 头文件iostream声明了一个cout的ostream变量
    4. 必须指明名称空间std
    5. 必须包含头文件fstream
    6. 头文件fstream定义了一个用于处理输出的ofstream类
    7. 需要将ofstream对象与文件关联起来,使用open方法
    8. 使用完文件后,使用close方法关闭

写入文件的例子

#include<iostream>
#include<fstream>
using namespace std;

int main()

{
    ofstream outFile;
    outFile.open("fish.txt");
    double wt = 12.2342;
    char name[10] = "dream.";
    outFile << wt << endl;
    outFile << name;
    system("pause");
    return 0;
}

每次写入文件会将原来的内容清空

  • 读取文本文件中
    1. 必须包含头文件fstream
    2. 头文件fstream定义了一个用于处理输入的ifstream类
    3. 需要声明一个或者多个ifstream变量
    4. 指明名称空间std
    5. 需要将ifstream对象和文件关联起来,使用open方法
    6. 使用完文件后,使用close方法将其关闭
    7. 使用ifstream对象和运算符>>来读取各种类型的数据
    8. 使用ifstream对象和get(getline)方法读取一个(一行)字符
    9. ifstream和eof()\fail()等方法判断输入是否成功
    10. ifstream对象本身被用作测试条件时,如果最后一个读取操作成功,被转换为true

读取文本文件的例子

#include<iostream>
#include<fstream>
using namespace std;

int main()

{

    ifstream inFile;
    inFile.open("fish.txt");
    char wt[10];
    char name[10];
    inFile.get(name,10);
    inFile.get();
    inFile.getline(wt, 10);
    //infile >> wt;
    //infile >> name;
    cout << wt << endl;
    cout << name << endl;
    system("pause");
    return 0;
}

增加inFile.get()语句是因为,读取完文件中第一行内容后,有一个回车,需要用这条语句吸收回车,再进入到第二行读取,否则,第二个数组读取到空格即停止读取

如果试图打开一个不存在的文件用于输入,将导致输入时失败,所以可以先判断文件是否被打开,is_open(),比如下面的例子

#include<iostream>
#include<fstream>
using namespace std;

int main()

{

    ifstream inFile;
    inFile.open("exe.txt");
    if (!inFile.is_open())
    {
        cout << "no file\n";
        //exit(EXIT_FAILURE);
    }
    cout << "fa\n";
    system("pause");
    return 0;
}

读取文件的时候不应该超过EOF,如果最后一次读取数据的时候遇到EOF,方法eof()将返回true,程序可能遇到类型不匹配的情况,方法fail()将返回true,出现意外的情况方法bad()返回true。更简单的方法是使用good()方法,没有任何错误的时候返回true

while(inFile.good())
{
    inFile >> value;
}

函数

编写自己函数的规则:
1. 提供函数定义
2. 提供函数原型
3. 调用函数

一个函数例子

#include<iostream>
using namespace std;

void Put(double d);//函数原型

int main()

{
    double w = 1.2;
    Put(w);//函数调用
    system("pause");
    return 0;
}

void Put(double s)//函数定义
{
    cout << s << endl;
}
  1. 为什么需要原型

原型描述了函数到编译器的接口,将函数的返回值类型和参数的类型和数量告诉编译器,函数完成计算候,把返回值放在指定的位置,编译器知道检索多少个字节,以及如何解释他们

  1. 原型的语法

复制函数定义的函数头加分号,函数原型不要求提供参数的变量名

  1. 原型的功能

    • 编译器正确处理函数返回值
    • 检查使用的参数数目是否正确
    • 检查参数类型是否正确

函数和数组

一个函数和数组的例子

#include<iostream>
using namespace std;

const int ArSize = 8;
int sum_arr(int arr[], int n);

int main()

{
    double w = 1.2;
    int cookies[ArSize] = { 1,4,2,5,6,6,7,3 };
    int sum = sum_arr(cookies, ArSize);
    cout << sum << endl;
    system("pause");
    return 0;
}

int sum_arr(int arr[], int n)
{
    int total = 0;
    for (int i = 0; i < n; i++)
    {
        total = total + arr[i];
    }
    return total;
}

方括号为空表明可以将任何长度的数组传递给该函数,cookies是一个指针,因此该函数传递的是数组第一个元素的地址,所以下面的函数头也是正确的

int sum_arr(int * arr,int n)

int arr[]表明arr不仅指向int还指向int数组的第一个int,而在其他的上下文中,这二者的含义并不相同

实际上函数并没有将数组内容传递给函数,而是将数组的地址、元素种类和元素数目传递给函数,传递常规变量时,函数将使用变量的拷贝,但是传递数组时,函数将使用原来的数组
思考:为什么不能直接使用sizeof arr确定数组长度?
cookies和arr指向同一个地址,但是sizeof cookies的值是32,是整个数组的长度,而sizeof arr是4,是指针变量的长度所以不能用sizeof arr

好处:将数组的地址作为参数可以节省复制整个数组所需的时间和内存,而且使用原始数组增加了破坏数据的风险

  • 一个函数和数组的例子,如何更改数组和保护数组:
#include<iostream>

const int Max = 5;

int fill_array(double arr[], int n);//数组可以改变
void show_array(const double arr[], int n);//const 保护数组不被改变
void revalue(double d, double arr[], int n);//可以改变

int main()

{
    using namespace std;
    double properties[Max];

    int size = fill_array(properties, Max);
    if (size > 0)
    {
        cout << "enter revaluation factor: ";
        double factor;
        while (!(cin >> factor))//检测是否有不好的输入
        {
            cin.clear();
            while (cin.get() != '\n')
                continue;
            cout << "bad input";
        }
        revalue(factor, properties, size);
        show_array(properties, size);
    }
    cout << "done\n";
    system("pause");
    return 0;
}

int fill_array(double arr[], int limit)
{
    using namespace std;//在需要cout和cin的函数中应用
    double temp;
    int i;
    for (i = 0; i < limit; i++)
    {
        cout << "enter value #" << (i + 1) << ": ";
        cin >> temp;
        if (!cin)//bad input
        {
            cin.clear();
            while (cin.get() != '\n')
            {
                continue;
            }
            cout << "bad input:input process terminated\n";
            break;
        }
        else if(temp < 0)
        {
            break;
        }
        else
        {
            arr[i] = temp;
        }
    }
    return i;
}

void show_array(const double arr[], int n)
{
    using namespace std;
    for (int i = 0; i < n; i++)
    {
        cout << "property #" << (i + 1) << ":$";
        cout << arr[i] << endl;
    }
}

void revalue(double r, double arr[], int n)
{
    for (int i = 0; i < n; i++)
    {
        arr[i] *= r;
    }
}
  • 另一个函数与数组的例子,使用数组区间
#include<iostream>

const int Max = 5;
int sum_arr(const int * begin, const int * end);//注意这里传递的是指针
int main()

{
    using namespace std;
    int properties[Max] = {1,2,1,6,4};

    int size = sum_arr(properties, properties + Max);
    cout << size << endl;
    system("pause");
    return 0;
}

int sum_arr(const int * begin, const int * end)//const说明不可以通过指针修改数组
{
    const int * pt;
    int total = 0;
    for (pt = begin; pt != end; pt++)
        total = total + *pt;
    return total;
}

指针和const:常规变量的地址可以赋给常规指针;常规变量的地址可以赋给指向const的指针;const变量的地址可以赋给指向const的指针;但是const的地址赋给常规指针不可行,即下面的代码不可行

const int a = 1;
int * a_add = &a;

函数和二维数组,这个也是写函数的时候最棘手的问题

一个二位数组作为参数的例子

#include<iostream>

const int Max = 5;
int sum_arr(int arr[][4], int size);
int main()

{
    using namespace std;
    int properties[Max][4] = {{1,2,1,6},{4,2,4,1}};

    int size = sum_arr(properties, 2);
    cout << size << endl;
    system("pause");
    return 0;
}

int sum_arr(int arr[][4], int size)
{
    int total = 0;
    for(int r = 0;r < size;r++)
        for (int c = 0; c < 4; c++) 
        {
            total = total + arr[r][c];
        }
    return total;
}

函数头的写法:

int sum_arr(int arr[][4],int size)
int sum_arr(int (*arr)[4],int size)

这两种都是可以的,指针的外围括号是必须的,size表示行数,4表示列数,所以可以想正常数组一样访问二维数组,声明一个4维指针数组,只不过限定了数组的列数

字符串和函数

一个字符串函数的例子

#include<iostream>
unsigned int c_in_str(const char * str, char ch);
int main()
{
    using namespace std;
    char a[15] = "emmm...";
    char * wail = "unbantu";
    unsigned int ms = c_in_str(a, 'm');
    unsigned int us = c_in_str(wail, 'u');
    cout << ms << us << endl;
    system("pause");
    return 0;
}

unsigned int c_in_str(const char * str, char ch)
{
    unsigned int count = 0;
    while (*str)
    {
        if (*str == ch)
            count++;
        str++;
    }
    return count;
}

字符串第一个元素的地址作为参数传入,字符串的最后一个字符是 ‘\0’,可以判断结束表示,所以不用传入数组长度,while(*str)

如果要返回字符串函数,可以返回字符串的地址,这样的效率更高

#include<iostream>
char * buildstr(char c, int n);
int main()
{
    using namespace std;
    char * wail = buildstr('c',10);
    cout << sizeof(wail) << endl;//4指向第一个元素的地址
    cout << wail << endl;
    delete[] wail;
    system("pause");
    return 0;
}

char * buildstr(char c, int n)
{
    char * pstr = new char[n + 1];//new一个n个字符的数组,最后一位存储'\0'
    //pstr指向数组的第一个元素的地址
    pstr[n] = '\0';
    while (n-- > 0)//先执行n>0逻辑运算,然后再执行n--,再执行循环体
    {
        pstr[n] = c;
    }
    return pstr;
}

变量pstr的作用于在函数内,所以函数结束时,pstr(而不是字符串)使用的内存将被释放

main中的当字符串不再需要的时候,用delete释放内存

问题:怎样区分一个指针指向整个数组地址还是数组首地址?
问题:函数中的new的地址已经在函数结束的时候释放掉为什么在主函数中还需要delete,new和delete配对使用?

让函数返回一个指针,该指针指向new分配的内存的缺点时,程序员必须记住使用delete,在12章,将知道c++类如何使用==构造函数和析构函数==处理这些细节

函数和结构

函数使用原始结构的副本,函数也可以返回结构;
与数组名就是数组第一个元素的地址不同的是,结构名只是结构的名称,要获得结构的地址,必须使用地址运算符&,c++还是使用这个运算符表示引用变量

  • 传递和返回结构
#include<iostream>

const int Mins_per_hr = 60;
struct travel_time
{
    int hours;
    int mins;
};
travel_time sum(travel_time t1, travel_time t2);//放在结构定义下面
void show_time(travel_time t);
int main()
{
    using namespace std;
    travel_time day1 = { 5,45 };
    travel_time day2 = { 4,55 };
    travel_time trip = sum(day1, day2);
    show_time(trip);
    system("pause");
    return 0;
}

travel_time sum(travel_time t1, travel_time t2)//返回结构
{
    travel_time total;
    total.mins = (t1.mins + t2.mins) % Mins_per_hr;
    total.hours = t1.hours + t2.hours + (t1.mins + t2.mins) / Mins_per_hr;
    return total;
}

void show_time(travel_time t)//传递结构
{
    using namespace std;
    cout << t.hours << "hours, " << t.mins << " minutes\n";
}
  • 传递结构的地址
    上面的例子修改如下:
#include<iostream>

const int Mins_per_hr = 60;
struct travel_time
{
    int hours;
    int mins;
};
travel_time sum(const travel_time * t1, const travel_time * t2);
void show_time(const travel_time * t);
int main()
{
    using namespace std;
    travel_time day1 = { 5,45 };
    travel_time day2 = { 4,55 };
    travel_time trip = sum(&day1, &day2);
    show_time(&trip);
    system("pause");
    return 0;
}

travel_time sum(const travel_time * t1, const travel_time * t2)
{
    travel_time total;
    total.mins = (t1->mins + t2->mins) % Mins_per_hr;
    total.hours = t1->hours + t2->hours + (t1->mins + t2->mins) / Mins_per_hr;
    return total;
}

void show_time(const travel_time * t)
{
    using namespace std;
    cout << t->hours << "hours, " << t->mins << " minutes\n";
}

函数和string对象

#include<iostream>
#include<string>
using namespace std;
const int Size = 5;
void display(const string sa, int n);
int main()
{
    string list = "you are very good.";
    display(list, Size);
    system("pause");
    return 0;
}

void display(const string sa, int n)
{
    cout << sa << endl;
}
#include<iostream>
#include<string>
using namespace std;
const int Size = 5;
void display(const string sa[], int n);
int main()
{
    string list[Size];//string类型的数组
    for (int i = 0; i < Size; i++)
    {
        cout << i + 1 << ": ";
        getline(cin, list[i]);
    }
    display(list, Size);
    system("pause");
    return 0;
}

void display(const string sa[], int n)
{
    for (int i = 0; i < n; i++)
    {
        cout << i + 1 << ": " << sa[i] << endl;
    }
}

函数和array对象

#include<iostream>
#include<array>
#include<string>
using namespace std;
const int Seasons = 5;
const array<string, Seasons> Snames = { "spring","summer","fall","winter" };
void show(array<double, Seasons> da);
void fill(array<double, Seasons> * pa);
int main()
{
    array<double, Seasons> expenses;
    fill(&expenses);
    show(expenses);
    system("pause");
    return 0;
}

void fill(array<double, Seasons> * pa)//需要对pa进行修改,所以是引用
{
    for (int i = 0; i < Seasons; i++)
    {//Snames是全局变量,可以直接调用
        cout << "enter " << Snames[i] << "expenses: ";
        cin >> (*pa)[i];
    }
}
void show(array<double, Seasons> da)
{
    double total = 0.0;
    cout << "\nexpenses\n";
    for (int i = 0; i < Seasons; i++)
    {
        cout << Snames[i] << ":$" << da[i] << endl;
        total += da[i];
    }
    cout << "total expenses:$" << total << endl;
}

递归

函数指针

函数也有地址,函数的地址是存储其机器语言代码的内存的开始地址,可以编写将另一个函数的地址作为参数的函数,这样第一个函数能够找到第二个函数并运行,它允许在不同的时间传递不同函数的地址

  1. 获取函数地址
    要将函数作为参数传递,必须传递函数名。区分函数地址和函数返回值

  2. 声明函数指针
    声明指定函数的返回类型以及函数的参数列表

double pam(int);//函数原型
double (*pf)(int);//声明函数指针

技巧:编写函数原型,然后用(*指针名)替换函数名

  1. 使用函数指针来调用函数

一个函数指针的例子

#include<iostream>

using namespace std;
double betsy(int lns);
void estimate(int lines, double(*pf)(int));
int main()
{
    int code = 7;
    estimate(code, betsy);//调用函数指针
    system("pause");
    return 0;
}

double betsy(int lns)
{
    return 0.05 * lns;
}
void estimate(int lines, double(*pf)(int))
{
    cout << lines << " lines will tak ";
    cout << (*pf)(lines) << " hours\n";//调用函数
}

简化声明的工具

auto
typedef

能够帮助创建类型别名typedef double real;

第8章 函数探幽

第9章内存模型和名称空间

第10章 对象和类

实际编程中已经实现了一些对象编程,有先验经验,不做过多的记录

类规范组成:
- 类声明:以数据成员的方式描述数据部分,以成员函数的方式描述公有接口
- 类方法定义:描述如何实现成员函数

声明Stock类类型的变量,称为对象或实例

使用类对象的程序都可以直接访问公有部分,但只能通过公有成员函数或者友元函数来访问对象的私有成员。所以公有成员函数是程序和对象的私有成员之间的桥梁。
类涉及尽可能将公有接口和实现细节分开,将实现细节放在一起并将他们与抽象分开被称为封装。

数据项通常放在私有部分,组成类接口的成员函数放在公有部分
不必在类声明中使用关键字private,对类对象的默认访问控制

  • 实现类成员函数
    定义成员函数时,使用作用域解析运算符::来表示函数所属的类;
    类方法可以访问类的private组件
void Stock::update(double price)

意味着定义的函数是Stock成员

Stock类的其他成员函数不必使用作用域解析运算符,就可以使用update()方法。

内联方法:
其定义位于类声明中的函数都将自动称为内联函数。
也可以在类声明之外定义成员函数,并使其称为内联函数,只需在类实现部分中定义函数时使用inline限定符

所创建的每个新对象都有自己的存储空间,存储内部变量和类成员,但是同一个类的所有对象共享同一组类方法,即每种方法只有一个副本

一个实现面向对象编程的例子
Stock.h

#pragma once
#include<string>
class Stock
{
    std::string company;
    long shares;
    double share_val;
    double total_val;
    void set_tot() { total_val = shares * share_val; }
public:
    void acquire(const std::string & co, long n, double pr);
    void buy(long num, double price);
    void sell(long num, double pricec);
    void update(double price);
    void show();
    Stock();//构造函数
    ~Stock();//析构函数
};

Stock.cpp

#include<iostream>
#include "Stock.h"

void Stock::acquire(const std::string & co, long n, double pr)
{//管理某个公司股票首次购买
    company = co;
    if (n < 0)
    {
        std::cout << "number of shares cant be negative; "
            << company << "shares set to 0.\n";
        shares = 0;
    }
    else
    {
        shares = n;
    }
    share_val = pr;
    set_tot();
}

void Stock::buy(long num, double price)
{
    if (num < 0)
    {
        std::cout << "number of shares purchased cant be negative. "
            << "transaction is aborted.\n";
    }
    else {
        shares += num;
        share_val = price;
        set_tot();
    }
}

void Stock::sell(long num, double price)
{
    using std::cout;
    if (num < 0)
    {
        cout << "number of shares sold cant be negative.\n";
    }
    else if (num > shares)
    {//用户试图卖出超过他持有的股票数量
        cout << "you cant sell more than you have!\n";
    }
    else {
        shares -= num;
        share_val = price;
        set_tot();
    }
}

void Stock::update(double price)
{
    share_val = price;
    set_tot();
}

void Stock::show()
{
    std::cout << "company: " << company << " shares: " << shares
        << "\n" << "share price:$" << share_val
        << " total worth:$" << total_val << "\n";
}

Stock::Stock()//类构造函数
{
    company = "no name";
    shares = 0;
    share_val = 0.0;
    total_val = 0.0;
}


//Stock::~Stock()
//{
//}

Source.cpp

#include<iostream>
#include "Stock.h"

int main()
{
    Stock fluffy_the_cat;
    fluffy_the_cat.acquire("NanoSmart", 20, 12.5);
    fluffy_the_cat.show();
    fluffy_the_cat.buy(15, 19.2);
    fluffy_the_cat.show();
    fluffy_the_cat.sell(400, 20.0);
    fluffy_the_cat.show();
    fluffy_the_cat.buy(30000, 40.12);
    fluffy_the_cat.show();
    fluffy_the_cat.sell(30000, 0.12);
    fluffy_the_cat.show();
    system("pause");
    return 0;
}

延伸:控制格式cout.setf() cout.precision(3)

类的构造函数和析构函数

为类提供被称为构造函数和析构函数的标准函数
c++的目标之一是让使用类对象就像使用标准类型一样,主要问题在于数据成员是私有的

解决方法是创造新对象时,自动对它初始化,c++提供了一个特殊的成员函数,类构造函数,专门用于构造新对象,将值赋给他的数据成员。Stock类一个可能的构造函数是名为Stock()的成员函数。构造函数没有声明类型

声明和定义构造函数

通常应提供所有类成员做隐式初始化的默认构造函数

初始化:

Stock first;
Stock first = Stock();
Stock * prelief  = new Stock;

析构函数

用构造函数创建对象后,程序负责跟踪该对象,直到过期为止,对象过期时,程序将自动调用一个特殊的成员函数——析构函数,完成清理工作
析构函数的名称在类名前加上~

析构函数不承担任何重要工作,可以编写为不执行任何操作的函数

通常不应再代码中显式地调用析构函数,如果创建的是静态存储类对象,析构函数再程序结束时自动被调用,如果创建的时自动存储类对象,将再程序执行完代码块时自动被调用。如果对象是通过new创建的,将驻留再栈内存或者自由存储区,使用delete来释放内存时,析构函数将自动被调用

修改上面例子如下:

#pragma once
#include<string>
class Stock
{
    std::string company;
    long shares;
    double share_val;
    double total_val;
    void set_tot() { total_val = shares * share_val; }
public:
    void acquire(const std::string & co, long n, double pr);
    void buy(long num, double price);
    void sell(long num, double pricec);
    void update(double price);
    void show();
    Stock();//构造函数
    Stock::Stock(const std::string & co, long n, double pr);//另一个构造函数,函数重载
    ~Stock();//析构函数
};

#include<iostream>
#include "Stock.h"

void Stock::acquire(const std::string & co, long n, double pr)
{//管理某个公司股票首次购买
    company = co;
    if (n < 0)
    {
        std::cout << "number of shares cant be negative; "
            << company << "shares set to 0.\n";
        shares = 0;
    }
    else
    {
        shares = n;
    }
    share_val = pr;
    set_tot();
}

void Stock::buy(long num, double price)
{
    if (num < 0)
    {
        std::cout << "number of shares purchased cant be negative. "
            << "transaction is aborted.\n";
    }
    else {
        shares += num;
        share_val = price;
        set_tot();
    }
}

void Stock::sell(long num, double price)
{
    using std::cout;
    if (num < 0)
    {
        cout << "number of shares sold cant be negative.\n";
    }
    else if (num > shares)
    {//用户试图卖出超过他持有的股票数量
        cout << "you cant sell more than you have!\n";
    }
    else {
        shares -= num;
        share_val = price;
        set_tot();
    }
}

void Stock::update(double price)
{
    share_val = price;
    set_tot();
}

void Stock::show()
{
    std::cout << "company: " << company << " shares: " << shares
        << "\n" << "share price:$" << share_val
        << " total worth:$" << total_val << "\n";
}

Stock::Stock()
{
    company = "no name";
    shares = 0;
    share_val = 0.0;
    total_val = 0.0;
}

Stock::Stock(const std::string & co, long n, double pr)
{
    std::cout << "constructor using " << co << " called\n";
    company = co;
    if (n < 0)
    {
        std::cout << "number of shares cant be negative.\n";
        shares = 0;
    }
    else
        shares = n;
    share_val = pr;
    set_tot();
}

Stock::~Stock()
{
    std::cout << "bye, " << company << std::endl;
}

#include<iostream>
#include<string>
#include "Stock.h"

int main()
{
    {
        Stock stock1("NanoSmart", 12, 20.0);
        stock1.show();
        Stock stock2 = Stock("Boffo", 2, 2.0);
        stock2.show();
        stock1 = stock2;

    }
    system("pause");
    return 0;

}

在主函数中多了一个大括号,析构函数在程序退出其定义所属的代码块时消失,如果没有大括号,代码块将为整个main(),main()执行完毕后调用析构函数,就看不到输出了

注意程序初始化的方式:

        Stock stock1("NanoSmart", 12, 20.0);
        Stock stock2 = Stock("Boffo", 2, 2.0);

如果构造函数使用了new,则必须提供使用delete的析构函数

this指针

每个成员函数都有一个this指针,this指针指向调用对象,如果方法需要引用整个调用对象,则可以使用表达式*this,在函数的括号后面使用const限定符,则不能使用this来修改对象的值,this是指隐式对象的地址,对象本身为*this
将下面这段代码加入到stock.cpp文件中

const Stock & Stock::topval(const Stock & s) const
{
    if (s.total_val > total_val)
        return s;
    else
        return *this;//返回隐式对象
}

对象数组

void Stock::show() const
{
    using std::ios_base;
    using std::cout;
    ios_base::fmtflags orig = cout.setf(ios_base::fixed, ios_base::floatfield);//fixed是枚举量
    std::streamsize prec = cout.precision(3);
    std::cout << "company: " << company << " shares: " << shares
        << "\n" << "share price:$" << share_val;
    cout.precision(2);
    cout<< " total worth:$" << total_val << "\n";
    cout.setf(orig, ios_base::floatfield);
    cout.precision(prec);
}
#include<iostream>
#include<string>
#include "Stock.h"

int main()
{
    const int size = 2;
    Stock stocks[size] = {
        Stock("nano",12.5l,20),
        Stock("boffo", 20.2l, 1200)
    };
    const Stock * top = &stocks[0];//这里还需要再明确
    top = &top->topval(stocks[1]);
    top->show();
    system("pause");
    return 0;

}

类作用域

在类中定义的名称的作用域都为整个类,使用类成员名时,必须根据上下文使用直接成员运算符.,间接成员运算符->或作用域解析运算符::
作用域为类的常量——
1. 使用枚举

class Bakery
{
    private:
        enum {Months = 12};
        double cots[Months];
        ...
}
  1. 使用关键字static
class Bakery
{
private:
    static const int Months = 12;
    double costs[Months];
    ...
}

该变量与其他静态变量存储在一起,而不是存储在对象中,所有Bakery对象共享这一个变量

抽象数据类型

emmm,看到这里好像看到了我原来面试的一道题

#pragma once
typedef unsigned long Item;//重新命名unsigned long
class Stack
{
    enum {MAX = 10};
    Item items[MAX];//栈中存储的数据
    int top;//栈顶index,指向栈顶为空的位置
public:
    Stack();
    bool isempty() const;
    bool isfull() const;//隐式对象不可变
    bool push(const Item & item);//隐式对象有变化
    bool pop(Item & item);
    ~Stack();
};
#include "Stack.h"



Stack::Stack()//创建空栈
{
    top = 0;
}
bool Stack::isempty() const
{
    return top == 0;
}
bool Stack::isfull() const
{
    return top == MAX;
}
bool Stack::push(const Item & item)
{
    if (top < MAX)
    {
        items[top++] = item;//先赋值,top++
        return true;
    }
    else
        return false;
}
bool Stack::pop(Item & item)
{
    if (top > 0)
    {
        item = items[--top];//top--,赋值
        return true;
    }
    else
        return false;
}


Stack::~Stack()
{
}
#include<iostream>
#include<string>
#include "Stock.h"
#include "Stack.h"
using namespace std;
int main()
{
    Stack st;
    char ch;
    unsigned long po;
    cout << "pls. enter A to add a purchase order,\n"
        << "P to process a PO,or Q to quit,\n";
    while (cin >> ch && toupper(ch) != 'Q')
    {
        while (cin.get() != '\n')
            continue;
        if (!isalpha(ch))
        {
            cout << '\a';
            continue;
        }
        switch (ch)
        {
        case 'A':
        case 'a':cout << "enter a po number to add: ";
            cin >> po;
            if (st.isfull())
                cout << "stack is full\n";
            else
                st.push(po);
            break;
        case 'P':
        case 'p':
            if (st.isempty())
                cout << "stack is empty\n";
            else {
                st.pop(po);
                cout << po << " popped\n";
            }
            break;
        default:
            break;
        }
    }

    system("pause");
    return 0;

}

第11章 使用类

运算符重载,应用于类对象

operator op()

operator +() operator *()

友元函数,允许访问私有数据

重载<<运算符,用于输出

状态成员

rand生成随机值

类的自动转换和强制类型转换

类转换函数

先看到这里,需要提升的时候再接着看

猜你喜欢

转载自blog.csdn.net/weixin_36926779/article/details/81146809