书签:《c++第6版》8 章 p266
1、常识
note
- 对象是类的特定实例,而类定义了数据的存储和使用方式。
- 头文件
climits
定义了符号常量来表示类型的限制。 - 警告:如果不对函数内部定义的变量进行初始化,该变量的值将是不确定的。这意味着该变量的值将是它被创建之前,相应内存单元保存的值。在声明变量时对它进行初始化,可避免以后忘记给它赋值的情况发生。
- 可以用整个名称大写的字母表示常量(const)
- 可以使用
cin.get()
让程序暂停一下。
输入输出:
#include <iostream>
using namespace std;
cout << "Hello World!\n";
int a;
cin >> a;
cout << "a=" << a << endl;
==========
Hello World!
8
a=8
-----------------------
wchar_t bob = L'P'; //宽字节,2个字节-8位,整数类型
wcout << L"tall" << endl;
char16_t ch1 = u'q';//C++11 使用前缀u表示char16_t字符常量和字符串常量,如uC'和u"be good";
char32_t ch2 = U'\U0000222B';//C++11 使用前缀U表示char32_t常量
2、简单类型
C++的基本类型分为两组:一组由存储为整数的值组成,另一组由存储为浮点格式的值组成。整型之间通过存储值时使用的内存量及有无符号来区分。整型从最小到最大依次是: bool、char、signed char、unsigned char、short、unsigned short、int、unsigned int、long、unsigned long
以及C++11新增的 long long
和unsigned long long
。还有一种wchar_t
类型,它在这个序列中的位置取决于实现。C++11新增了类型char16_t
和char32_t
,它们的宽度足以分别存储16和32位的字符编码。C++确保了char
足够大,能够存储系统基本字符集中的任何成员,而wchar_t
则可以存储系统扩展字符集中的任意成员,short
至少为16位,而int
至少与short
一样长,long
至少为32位,且至少和int
一样长。确切的长度取决于实现。字符通过其数值编码来表示。I/O系统决定了编码是被解释为字符还是数字。
浮点类型可以表示小数值以及比整型能够表示的值大得多的值。3种浮点类型分别是float、double
和long double
。C++确保float
不比double
长,而double
不比long double
长。通常,float
使用32位内存, double
使用64位,long double
使用80到128位。
通过提供各种长度不同、有符号或无符号的类型,C++使程序员能够根据特定的数据要求选择合适的类型。
C++使用运算符来提供对数字类型的算术运算:+、-、*、/
和%
(取模)。当两个运算符对同一个操作数进行操作时,C++的优先级和结合性规则可以确定先执行哪种操作。
//默认格式为十进制,在修改格式之前,原来的格式将一直有效。
十进制、8进制、十六进制分别表示42
42 dec
052 oct
0x2a hex
cout<<hex;
类型间的相互转换
1)初始化和赋值进行的转换
so_long = thirty;// assigning a short to a long
//这个要注意不可浮点转整,高位转低位。也就是禁止缩窄转换
2)以{ }
方式初始化时进行的转换(C++11)
const int code = 66;
int x = 66;
char c1{ 31325 };// narrowing, not allowed
char c2 = { 66 };// allowed because char can hold 66
char c3{ code };// ditto
char c4 = { x };// not allowed, x is not constant
x = 31325;
char c5 = x;// allowed by this form of initialization
3)表达式中的转换
- a. 整型提升((integral promotion):在计算表达式时,C++将
bool、char、unsigned char、signed char
和short
值转换为int
。具体地说,true被转换为1,false被转换为0。通常将int类型选择为计算机最自然的类型,这意味着计算机使用这种类型时,运算速度可能最快。 - b. 将不同类型进行算术运算时,也会进行一些转换。当运算涉及两种类型时,较小的类型将被转换为较大的类型。
前面的列表谈到了整型级别的概念。简单地说,有符号整型按级别从高到低依次为long long、long、int、short
和signed char
。无符号整型的排列顺序与有符号整型相同。类型char、signed char
和unsigned char
的级别相同。类型bool
的级别最低。wchar_t、char16_t
和char32_t
的级别与其底层
类型相同
4)传递参数时的转换
- 传递参数时的类型转换通常由C++函数原型控制。
5)强制类型转换
(long) thorn // old C
long (thorn) // new c++
static_cast<long>(thorn) //returns a type long conversion of thorn
- C++11新增了一个工具,让编译器能够根据初始值的类型推断变量的类型。为此,它重新定义了
auto
的含义。auto
是一个C语言关键字,但很少使用。
std::vector<double> scores;
std::vector<double> ::iterator pv = scores.begin();
---------
std::vector<double> scores;
auto pv = scores.begin(); //省力气
3、复合类型
- 数组、结构和指针是C++的3种复合类型。数组可以在一个数据对象中存储多个同种类型的值。通过使用索引或下标,可以访问数组中各个元素。
- 结构可以将多个不同类型的值存储在同一个数据对象中,可以使用成员关系运算符(.)来访问其中的成员。使用结构的第一步是创建结构模板,它定义结构存储了哪些成员。模板的名称将成为新类型的标识符,然后就可以声明这种类型的结构变量。
- 共用体可以存储一个值,但是这个值可以是不同的类型,成员名指出了使用的模式。
指针是被设计用来存储地址的变量。我们说,指针指向它存储的地址。指针声明指出了指针指向的对象的类型。对指针应用解除引用运算符,将得到指针指向的位置中的值。 - 字符串是以空字符为结尾的一系列字符。字符串可用引号括起的字符串常量表示,其中隐式包含了结尾的空字符。可以将字符串存储在char数组中,可以用被初始化为指向字符串的
char
指针表示字符串。函数strlen()
返回字符串的长度,其中不包括空字符。函数strcpy()
将字符串从一个位置复制到另一个位置。
在使用这些函数时,应当包含头文件cstring
或string.h
。 - 头文件
string
支持的C++ string
类提供了另一种对用户更友好的字符串处理方法。具体地说,string
对象将根据要存储的字符串自动调整其大小,用户可以使用赋值运算符来复制字符串。
1.数组
typeName arrayName [arraysize];//声明数组的通用格式
int maxtemps[4][5] = //2-D array
{
{ 96,100,87,101,105 }, // values for maxtemps[0]
{ 96,98,91,107,104 }, // values for maxtemps[1]
{ 97,101,93,108,107 },// values for maxtemps[2]
{ 98,103,95,109,108 } // values for maxtemps[3]
}; // int a[2,3]; 没有这种形式的,在C#里面有
2.char[] 字符串
char cat[8] = {'f', 'a', 't','e','s','s','a ','\0'};
//最后一个字符必须是'\0'
//sizeof 运算符指出整个数组的长度,
//strlen()只计算可见的字符,而不把空字符计算在内
//cin 使用空白(空格、制表符和换行符)来确定字符串的结束位置
cin.getline(name,20);//getline()函数读取整行,它使用通过回车键输入的换行符来确定输入结尾。
cin.get(name,ArSize); // read first line
cin.get(); //read newline
cin.get(dessert, Arsize); // read second line
//get()不能跨过换行符
cin.get(name,ArSize).get();
// 之所以可以这样做,是由于cin.get(name,ArSize)返回一个cin对象,
//该对象随后将被用来调用get()函数
//上面两种方式与调用两次getline()效果相同
//getline()使用起来简单一些,但get()使得检查错误更简单些。
//通过查看下一个输入字符,如果是换行符,说明已读取了整行;否则,说明该行中还有其他输入。
3.string 对象
#include <string>
cin.getline(charr,20); //读取char[]数组
getline(cin,str); //读取string 对象
cout <<R"(Jim "King" Tutt uses "\n" instead of endl.)" << '\n';
//Jim "King"Tutt uses \n instead of endl.
//C++ 新增的原始(raw)字符串,以"( 和 )"表示定界符,也可以自定义定界符。
4.结构
struct inflatablell //structure declaration
{
char name [20];
float volume;
double price;
};
//初始化
inflatable duck {"Daphne",0.12,9.98}; // can omit the = in C++11
struct torgle_register
{
unsigned int sN : 4;// 4 bits for SN value
unsigned int : 4 ;// 4 bits unused
bool goodIn : 1;// valid input (1 bit)
bool goodTorgle : 1;// successful torgling
};
//字段的类型应为整型或枚举(稍后将介绍),接下来是冒号,
//冒号后面是一个数字,它指定了使用的位数。
//可以使用没有名称的字段来提供间距。每个成员都被称为位字段(bit field)。
torgle_register tr = { 14, true,false };//初始化
5.共用体
union one4all
{
int int_val;
long long_val;
double double_val;
};
//结构可以同时存储int、long 和 double,共用体只能存储int、long或double.
one4all pail;
pail.int_val = 15;// store an int
cout << pail.int_val;
pail.double_val = 1.38;// store a double, int value is lost
cout << pail.double_val;
//所以,共用体的长度为其最大成员的长度
struct widget
{
char brand[20];
int type;
union // anonymous union -匿名共用体,其成员将成为位于相同地址处的变量。
{
long id_num;// type 1 widgets
char id_char[20];//other widgets
};
};
widget prize;
if (prize.type == 1)
cin >> prize.id_num;
else
cin >> prize.id_char;
6.枚举
enum {red,orange,yellow,green,blue,violet,indigo,ultra,violet};
//枚举的规则相当严格。实际上,枚举更常被用来定义相关的符号常量,而不是新类型。
7.指针
int updates = 6;// declare a variable
int * p_updates;// declare pointer to an int
p_updates = &updates;// assign address of int to pointer
//下面的声明创建一个指针(p1)和一个int变量(p2):
int* p1, p2;//对每个指针变量名,都需要使用一个*。
//指针真正的用武之地在于,在运行阶段分配未命名的内存以存储值。
//在C语言中,可以用库函数malloc()来分配内存;
//C++中同样可以用,但是new 要更方便
int *pn = new int;
//为一个数据对象(可以是结构,也可以是基本类型)获得并指定分配内存的通用格式如下:
typeName * pointer_name = new typeName;
int * ps = new int; // allocate memory with new
... // use the memory
delete ps; // free memory with delete when done
使用new创建动态数组
int * psome = new int [10]; // get a block of 10 ints
delete psome; // free a dynamic array
//方括号告诉程序,应释放整个数组,而不仅仅是指针指向的元素。
总之,使用new 和delete时,应遵守以下规则。
- 不要使用
delete
来释放不是new
分配的内存。 - 不要使用
delete
释放同一个内存块两次。 - 如果使用
new[]
为数组分配内存,则应使用delete[]
来释放。 - 如果使用
new[]
为一个实体分配内存,则应使用delete
(没有方括号)来释放。 - 对空指针应用
delete
是安全的。
arrayname[i] becomes *(arrayname + i)
pointername [i] becomes *(pointername + i)
pointername - pointername + 1; \\ valid
arrayname = arrayname + 1; \\not allowed
//数组的动态联编
int size;
cin >> size;
int * pz = new int [size];// dynamic binding,size set at run time
...
delete [] pz;//free memory when finished
在多数情况下,C++将数组名视为数组的第一个元素的地址。一种例外情况是,将sizeof
运算符用于数组名用时,此时将返回整个数组的长度(单位为字节)。
如果结构标识符是结构名,则使用句点运算符;如果标识符是指向结构的指针,则使用箭头运算符。
struct inflatable // structure definition
{
char name [20];
float volume;
double price;
}
cin.get(ps->name,20;
cin >> (*ps).volume; //这样也可以代替->
cin >> ps->price;
----------------------------
//一个小函数-用char节省内存的做法,实际上用string 完全可以代替
char * getname()//return pointer to new string
{
char temp[80];// temporary storage
cout << "Enter last name: ";
cin >> temp;
char * pn = new char[strlen(temp) + 1];
strcpy(pn, temp); // copy string into smaller space
return pn; // temp lost when function ends
}
char * name;// create pointer but no storage
name = getname ( );//assign address of string to name
cout << name << " at " <<(int *) name << "\n" ;
delete [] name;//memory freed
name = getname ();//reuse freed memory
cout << name << " at " <<(int *) name << "\n" ;
delete [] name;//memory freed again
关于标准模板库中的vector、array
vector<typeName> vt(n_elem);
array<typeName, n_elem> arr;
- 无论是数组、vector对象还是array对象,都可使用标准数组表示法来访问各个元素。
- 其次,从地址可知,array对象和数组存储在相同的内存区域(即栈)中,而vector对象存储在另一个区域(自由存储区或堆)中。
- 第三,注意到可以将一个array对象赋给另一个array对象;而对于数组,必须逐元素复制数据。
4、循环和分支语句
这个用的最多了,不多说好吧。
for (initialization; test-expression; update-expression)
body
-----------------------
while (test-condition)
body
----------------------
do
body
while (test-expression) ;
----------------------
//C++11新增了一种循环:基于范围(range-based)的for循环。
//这简化了一种常见的循环任务:
//对数组(或容器类,如vector和array)的每个元素执行相同的操作,如下例所示:
double prices[5] = {4.99,10.99,6.87,7.99,8.49};
for (double x : prices)
cout << x << std: :endl;
//要修改数组的元素,需要使用不同的循环变量语法:
for (double &x : prices)
x=x * 0.80; //20% off sale
---------------------------------
while (cin.get(ch))// while input is successful
{
... // do stuff
}
//这样,cin.get(char)只被调用一次,而不是两次:循环前一次、循环结束后一次。
//为判断循环测试条件,程序必须首先调用cin.get(ch)。如果成功,则将值放入ch中。
//然后,程序获得函数调用的返回值,即cin。
//接下来,程序对cin进行bool转换,如果输入成功,则结果为true,否则为false。
//三条指导原则(确定结束条件、对条件进行初始化以及更新条件)全部被放在循环测试条件中。
-------------------------------------------
if(test-condition)
statement
-----------------------------------
if (test-condition)
statement1
else
statement2
--------------------------------
switch (integer - expression)
{
case label1: statement(s)
case label2: statement(s)
...
default: statement(s)
}
//C++中的case标签只是行标签,而不是选项之间的界线。
//也是说,程序跳到switch中特定代码行后,将依次执行之后的所有语句,
//除非有明确的其他指示。程序不会在执行到下一个case处自动停止,
//要让程序执行完一组特定语句后停止,必须使用break语句。
//这将导致程序跳到switch后面的语句处执行。
-------------------------------------------
//break和continue语句都使程序能够跳过部分代码。
//可以在switch语句或任何循环中使用break语句,使程序跳到switch或循环后面的语句处执行。
//continue语句用于循环中,让程序跳过循环体中余下的代码,并开始新一轮循环。
通常,cout
在显示bool
值之前将它们转换为int,但cout.setf (ios:: boolalpha)
函数调用设置了一个标记,该标记命令cout
显示true
和 false
,而不是1和0。
cout <<"The expression x > 3 has the value ";
cout << (x > 3) << endl; //output 0
cout.setf(ios_base:: boolalpha);//a newer C++ feature
cout << "The expression x < 3 has the value ";
cout << (x< 3) << endl; //output false
5、关于文件
使用文件输出的主要步骤如下:
1.包含头文件fstream。
2.创建一个ofstream对象。
3.将该ofstream对象同一个文件关联起来。
4.就像使用cout那样使用该ofstream对象。
//写文件
#include <fstream>//for file I/O
//可以作为写文件的模板
ofstream outFile;// create object for output
outFile.open ("carinfo.txt" );// associate with a file
outFile << "was asking $" << a_price << endl;
outFile <<"Now asking $" << d_price << endl;
outFile.close();// done with file,您忘记关闭文件,程序正常终止时将自动关闭它。
------------------------------------------------------
//读文件
#include <fstream>//for file I/O
//可以作为读文件的模板
ifstream inFile;//object for handling file input
inFile.open (filename);// associate inFile with a file
if(!inFile.is_open())// failed to open file
{
cout << "Could not open the file " << filename <c endl;
cout << "Program terminating. \n" ;
exit(EXIT_FAILURE);
}
double value;
double sum = 0.0;// number of items read
int count = o;
inFile >> value;// get first value
while (inFile.good())// while input good and not at EOF
{
++count;// one more item read
sum += value;// calculate running total
inFile >> value;// get next value
}
if(inFile.eof())
cout << "End of file reached.\n" ;
else if (inFile.fail())
cout <<" Input terminated by data mismatch.\n" ;
else
cout << " Input terminated for unknown reason.\n" ;
if(count == 0)
cout <<"No data processed.\n" ;
else
cout << "Items read: " << count c< endl ;
cout <<"sum:" << sum << endl;
cout <<"Average: " << sum / count << endl ;
inFile.close() ;// finished with the file
6、关于函数
- 提供函数定义;
- 提供函数原型;原型描述了函数到编译器的接口,也就是说,它将函数返回值的类型(如果有的话)以及参数的类型和数量告诉编译器。ANSIC中的原型是可选的,但在C++中,原型是必不可少的。
- 调用函数。
double cube (double x);//函数原型
//函数定义
typeName functionName (parameterList)
{
statements
return value;//value is type cast to type typeName
}
double volume = cube (side);//函数调用
int fil1_array(double ar[], int limit);//以数组为参数的函数
int sum(int ( *ar2)[4], int size);
int sum(int ar2 [][4], int size);//以二维数组为参数
unsigned int c_in_str(const char * str,char ch);//以C-风格字符串为参数
unsigned int c_in_str(const char str[],char ch)// also okay
char * buildstr(char c, int n);//返回C-风格字符串的函数
void display(const string sa[], int n);//以string对象为参数
void fill(std::array<double,seasons> * pa);//以array对象为参数
//递归
void recurs(argumentlist)
{
statements1
if(test)
recurs(arguments)
statements2
}//test最终将为false,调用链将断开。
函数指针
- 获取函数的地址,获取函数的地址很简单:只要使用函数名(后面不跟参数)即可。
- 声明一个函数指针;
- 使用函数指针来调用函数。
//一定要区分传递的是函数的地址,还是函数的返回值
process(think);// passes address of think() to process()
thought(think());// passes return value of think() to thought ()
//声明一个函数指针
double pam(int) ;//prototype-函数原型
double (*pf)(int);// pf points to a function that takes-函数指针
double (*pf)(int); // pf points to a function that returns double
double *pf(int);// pf() a function that returns a pointer-to-double
void estimate(int lines, double (*pf)(int));//以函数指针为参数
//用函数指针来调用函数
double pam(int);
double (*pf)(int) ;
pf = pam;// pf now points to the pam() function
double x = pam(4);// call pam() using the function name
double y = (*pf)(5); // call pam() using the pointer pf
下面这个里面,求定积分用到了函数指针。
https://blog.csdn.net/Gou_Hailong/article/details/108486365
下面深入探讨函数指针。下面是一些函数的原型,它们的特征标和返回类型相同:
const double * f1(const double ar[], int n);
const double * f2(const double [], int);
const double * f3(const double *, int);
const double *(*p1) (const double *, int) = f1;
//可在声明的同时进行初始化
auto p2 = f2;//C++1 automatic type deduction,简化好多
const double *(*pa[3])(const double *, int) = {f1,f2,f3};
//声明并初始化一个包含3个函数指针的数组。
pa
是一个包含三个元素的数组,而要声明这样的数组,首先需要使用pa[3]
。该声明的其他部分指出了数组包含的元素是什么样的。运算符]
的优先级高于*
,因此*pa[3]
表明pa
是一个包含三个指针的数组。上述声明的其他部分指出了每个指针指向的是什么:特征标为const double *, int,
且返回类型为const double*
的函数。因此,pa
是一个包含三个指针的数组,其中每个指针都指向这样的函数,即将const double*
和int
作为参数,并返回一个const double*
。注意,这里不能用auto
,自动类型推断只能用于单值初始化,而不能用于初始化列表。但声明数组pa
后,声明同样类型的数组就很简单了:auto pb=pa;
//调用
const double * px = pa[0] (av ,3);
const double * py = (*pb[1])(av , 3);
double x = *pa[o] (av,3 ) ;
double y = *(*pb[1])(av ,3);
auto pc = &pa; //C++11 automatic type deduction-指向数组的指针
const double *(*(*pd)[3])(const double *, int) = &pa;
//这俩一样,感谢auto
要调用函数,需认识到这样一点:既然pd
指向数组,那么*pd
就是数组,而(*pd)[i]
是数组中的元素,即函数指针。因此,较简单的函数调用是(*pd)[i](av,3)
,而*(*pd)[i](av,3)
是返回的指针指向的值。也可以使用第二种使用指针调用函数的语法:使用(*(*pd)[i])(av,3)
来调用函数,而*(*(*pd)[i])(av,3)
是指向的double
值。
//用typedef来简化
typedef const double *(*p_fun)(const double *, int);//p_fun now a type name
p_fun p1 = f1;// p1 points to the f1() function
p_fun pa[3] = {f1,f2,f3}; // pa an array of 3 function pointers
p_fun (*pd)[3] = &pa;// pd points to an array of 3 function pointers
总结
此遍淘书发现了许多之前没有注意到的地方,现汇总如下:
- 头文件,头文件带
.h
的有可能是C旧式风格,也有可能是C++旧式风格;前面带c
的是C新式风格,前面不带c
,后面不带.h
的是C++新式风格。 - 对象是类的特定实例,而类定义了数据的存储和使用方式。
auto
让编译器能够根据初始值的类型推断变量的类型,想偷懒的时候可以用- 关于内存的知识看基础知识那里。
- 语句块允许把两条或更多条语句放到按C++句法只能放一条语句的地方。- 逗号运算符
- C++为类型建立别名的方式有两种:
#define BYTE char
和typedef typeName aliasName;
- cin.get(ch)与cin.get()
属性 | cin.get(ch) | ch=cin.get() |
---|---|---|
传递输入字符的方式 | 赋给参数ch | 将函数返回值赋给ch |
用于字符输入时函数的返回值 | istream 对象(执行bool 转换后为true ) | int类型的字符编码 |
到达EOF 时函数的返回值 | istream 对象(执行bool 转换后为false ) | EOF |
- 用两种不同的方式将const关键字用于指针。第一种方式是让指针指向一个常量对象,这样可以防止使用该指针来修改所指向的值,第二种方式是将指针本身声明为常量,这样可以防止改变指针指向的位置。
int age = 39;
const int * pt = &age;
----------
const float g_earth = 9.80;
const float * pe = &g_earth; //VALID
const float g_moon = 1.63;
float * pm = &g_moon; //INVALID