C++PrimerPlus 知识总结一

来源:互联网 发布:股市行情数据接口 编辑:程序博客网 时间:2024/06/08 11:25

共用体

union Unioname
{
type1 name1;
type2 name2;
type3 name3;
}
Unionname name;
共用体与结构类型的不同在于共用体在内存中一次只能存储一种简单类型,而结构把所有类型都存储在内存中,所以共用体相对结构来说可以节省内存,其中一种用法就是把无名共用体放在结构当中,这样可以节省结构的内存。
struct a{char bck[20];int type;union{long id_num;char id_char[20];};};a pr;cin>>pr.id_num;cin>>pr.id_char;

枚举

枚举也有取值范围,可以通过强制类型转换将不是枚举但在取值范围内的值赋给枚举变量.C++11还增加了作用域内枚举

指针

注意:
在指针没有初始化之前是不能赋值的.
int* p;
*p=2333;
这样是不行的,在创建指针时,确实会分配一个指针类型的内存,但是指针并没有指向这段内存,所以在赋值的时候我们不知道2333会放在哪段内存中,也许会放在已经有数据的内存中,所以声明指针时一定要对指针进行初始化.

如果要为指针指定内存区域,要使用强制类型转换

例如:

int* p;

p=0xB8000000;

这样是不行的,这只是一个整数

要使用p= (int*)0xB8000000

使用new分配内存区域

new分配的内存区域与常规变量分配的内存区域是不同的,常规变量分配的内存区域叫做栈,而new分配的内存区域叫做自由存储区或堆,不同之处在于堆的内存要程序员手动释放.

因此要使用delete,如果堆内存被耗尽,使用new时通常会引发异常.

如果要声明一个数组,数组的长度需要用户提供的信息来决定,那么可以使用new创建动态数组,使用new时,如果在运行阶段需要数组,则创建它,如果不需要数组,则不创建,还可以在运行时决定数组长度,这称为动态联编.

使用new创建数组,只需要将数组的元素类型和元素数目告诉new即可.

int *p=new int [10];

delete []p;

new 运算符返回第一个元素的地址.

使用p[n]访问数组中的元素

假如有一个数组

int a[10];

则不能使用a+1访问数组的第二个元素.

但对于动态数组,可以使用p+1访问数组的第二个元素.

对常规数组使用sizeof时得到的是数组的整个长度,而对指针使用sizeof时得到的是指针的长度,即使指针指向的是一个数组.

数组名被解释为第一个元素的地址,而对数组名应用地址运算符时,得到的是整个数组的地址.

在c++中,用引号括起来的字符串像数组名一样,也是第一个元素的地址,如果给cout提供一个字符的地址,则它将从该字符开始打印,直到遇到空字符为止.

如果给cout提供一个指针,它将打印地址,如果给cout提供一个char* 则cout将显示指向的字符串.如果想要显示地址,则必须强制类型转换.

如果要将字符串赋给数组,则应该使用strcpy或strncpy,而不是使用赋值运算符.


文件输入输出

写入到文件
使用ofstream要指定名称空间std
#include<fstream>
ofstream outfile;
outfile.open("text.txt");
char a='d';
outfile<<a;
可以像使用cout那样使用ofstream 对象
程序使用完该文件时,应该将其关闭
outfile.close();
打开已有的文件,将内容输入到文件中,会丢弃原文件中的内容.

从文件输出
#include<fstream>
ifstream infile;
infile.open("文件");
int a;
infile>>a;
infile.close();
检查文件是否被成功打开的方法是is_open()
if(!infile.is_open())
{
exit();
}
exit()原型在cstdlib中定义.

文件结尾
windows 使用ctrl+z 和enter表示文件尾
unix使用ctrl+D来实现文件尾
文件尾的标志EOF
检测到EOF时,cin将eofbit和failbit都置为1,可以通过成员函数eof()来查看eofbit是否被设置.如果检测到eof,cin.eof()将返回true cin.fali()函数也将返回true
如果读取的文件损坏,则bad()函数将返回true,可以使用good()函数检测文件是否正常.

函数和数组

将数组传递给函数时,除了要传递数组名之外,还要传递数组长度.

传递数组将数组的地址传递给函数,与传递常规变量不同,常规变量传递的是变量的副本

因此,为了避免更改数组地址内的内容,一般使用const


const和指针

int a=100;
const int *p=&a;
表示p指向const int,不能使用p修改这个值,
*p=200//不行
但是可以通过a修改
a=200//可以
可以将const变量的地址赋给const指针,不能将const变量的地址赋给常规指针.,可以将非const的指针赋给const指针
仅当只有一层间接关系时,才可以将非const的地址或指针赋给const指针,一层间接关系是指指针指向基本数据类型,不是指针的指针.
将指针参数声明为指向常量数据的指针有两点理由:
1 可以避免无意间修改数据
2 使用const使得函数能够处理const和非const实参,否则只能处理非const数据.

int* const finger=&slon;
这种声明使得finger只能指向slon,但可以使用finger修改slon的值.

函数和结构

将结构体传递给函数时,最好传递结构的指针或引用.

函数指针

通常函数名就是函数地址,因此我们可以声明一个函数指针,声明的格式是将函数名替换为指针

double pam(int);

double (*p)(int);//声明一个返回值为double,参数类型为int的函数指针

然后可以将函数地址传递给指针

p=pan;

使用函数指针时,只需将函数指针看作函数名就行

double y=(*p)(5);


引用

引用的主要用途是用作函数参数,将引用用作函数参数函数将使用原始数据而不是副本,可以减少内存开销.

必须在声明引用的时候初始化

不能先声明再赋值
int rat;
int & robit;
robit=rat;//不行.
引用更接近于const指针,必须在创建时对他初始化,一旦与某个变量关联,就一直效忠它.
如果不想对变量进行更改,则可以使用常量引用.


默认参数
默认参数指出当函数调用中省略了实参时自动使用的一个值。
设置默认值必须通过函数原型
char* left(const char *str,int n=1);
对于带参数列表的函数,必须从右向左添加默认值,要为某个参数设置默认值,必须为它右边的所有参数提供默认值。

将引用用作返回值

如果要将引用用作返回值,则可以将整个函数表达式作为左值为函数表达式赋值,因为引用返回的是一段可更改的内存区域,但如果是常规函数,返回值将位于临时内存,不能更改.

如果不想更改返回值的内存,可以使用返回const引用.

将类对象传递给函数时,通常使用引用

基类引用可以指向派生类对象,可以定义一个接受基类引用作为参数的函数,调用该函数时,可以将基类对象作为参数,也可以将派生类对象作为参数.


函数重载引用参数

三种重载方式
int s1(int &a)//可以接受int类型
int s2(const int & a)//可以接受int类型,const int类型和两个int的和(右值)
int s3(int &&a)//只能接受右值.

函数模板

template<typename T>

显示具体化

当编译器找到与函数调用匹配的具体化定义时,将使用具体化定义,而不再寻找模板.
显示具体化的标准
1 对于给定的函数名,可以有非模版函数,模板函数和显示具体化模板函数和他们的重载版本.
2 显示具体化的原型和定义应以template<>打头,并通过名称来指出类型.
3 显示具体化优先于常规模板,非模板函数优先与具体化和常规模板
struct job
{};
显示具体化:template<>void swap<job>(job &,job &);

在代码中包含函数模板本身并不会生成函数定义,它只是一个用于生成函数定义的方案


编译器使用模板为特定类型生成函数定义时,,得到的是实例.这种实例方法被称为隐式实例化.
有隐式实例化就会有显示实例化
template void swap<int>(int ,int )
在同一个文件中使用同一种类型的显式实例化和显式具体化将出错.

头文件中常包含的内容

函数原型
使用#define或const定义的符号常量
结构声明
类声明
模板声明
内联函数

#ifndef 

#endif
可以避免多次包含同一个头文件.

存储持续性.作用域和链接性

自动存储持续性
静态存储持续性
线程存储持续性
动态存储持续性

首先是c++编译器对于代码在内存中的位置分配
c++编译器把内存分为栈区,堆区,全局变量区,文字常量区,代码区。

文字常量区和代码区主要就是定义的一些字面值常量或者函数体的二进制代码,主要的存储区还是堆区,栈区,和全局变量区
堆区是由new分配的内存块,这些区域不归编译器管,内存释放要由程序员自己释放,使用delete,堆内存的大小一般为4G,理论上为虚拟内存的大小,实际上可能是虚拟内存的大小减1G,当使用new找不到请求的内存量时,将返回空指针或引发异常std::bad_alloc。
自动变量存储在栈区中,作用域为当前代码块,由编译器自动分配与释放,编译器默认分配的栈的大小为1M,可以在编译器中修改栈的大小,栈一般和线程有关,堆和进程有关。
栈的生长方向是向下生长的,向着内存地址减小的方向生长,而堆是向着内存地址增加的方向增长。还可以在变量名前加register关键字,这样会使用cpu寄存器来存储变量,速度更快。
一般对于堆来说,频繁地new/delete会造成内存空间不连续,造成大量的碎片,而对于栈不会出现这样的情况。
全局变量区内的变量是使用static关键字或在文件全局区内声明的变量,在程序开始时为这些变量分配内存并初始化,一般初始化为0,在程序结束时释放内存。

还有就是自由存储区,使用malloc分配的内存,和堆差不多。
然后是各种存储持续性
自动存储持续性就是在栈或使用register关键字声明的变量,作用域为代码块内

静态存储持续性为在全局变量区内的变量,其中的变量又分为外部链接性,内部连接性,无链接性。外部链接性意思就是在整个文件中都可以使用这个变量,也可以在其他文件中使用,定义方法是在文件全局区内声明变量,但根据c++的单定义规则,一个变量只能定义一次,所以在其他文件中,要使用这个变量,在定义的时候要加extern关键字。
内部链接性是指在文件全局区声明变量,并加上static关键字,这样的变量在当前文件中可用,在其他文件中不能用。还有无链接性,意思是在函数内声明的变量加static关键字,这样的变量在函数内可以使用,在函数外就不能再使用了。

线程存储持续性,将计算放在可并行处理的多个线程中,使用thread_local声明的变量,周期与所属的线程一样长

动态存储持续性,使用new分配的内存内的变量。

说明符与限定符

mutable 可以用它来指出,即使结构或类变量为const,其某个成员也是可以被修改的
struct a
{
mutable int a;
char b;
};
const a c;
c.a=10;
c.a++;//虽然为const 但是可以修改

虽然全局变量的链接性为外部,单如果加const 关键字,则链接性将变为内部,相当于又加上了static关键字。如果想让const变量的链接性为外部,则需要使用extern关键字,注意,是在所有的文件中都要使用,包括定义的文件。

函数也有链接性,通常情况下函数的链接性为外部,可以在不同的文件中共享,还可以使用关键字static把函数的链接性定义为内部,只能在当前文件中使用。
这意味着如果外部有一个同名的函数,当前文件中的函数将覆盖外部函数的定义。


定位new运算符

要使用定位new运算符,要包含头文件#include<new>
char buffer1[50];
char buffer2[500];
chaff *p1=new(buffer1) chaff;//chaff是结构
int *p2=new(buffer2+3*sizeof(int)) int[20];
p1指向的地址就是buffer1的地址。
定位new运算符不能使用delete,因为分配的不是堆上的内存。
定位new运算符一般与硬件编程有关,知道了一个硬件设备的地址,想要将c++类与硬件设备关联起来,可以使用定位new运算符。

名称空间

using声明和using编译指令
using声明将名称空间一部分变量可用,例如 using std::cout;
using编译指令将名称空间中所有变量可用 using namespace std;
名称空间可以创建别名
namesapce a
{
}
namespace b=a;

也可以声明一个无名的名称空间
无名的名称空间中的变量相当于全部加上static关键字,连接性为内部。