C++ primer (2) —— 基础

网友投稿 831 2022-08-27

C++ primer (2) —— 基础

C++ primer (2) —— 基础

一年前的部分学习笔记,现在整理并复习它们。

八 域和生命期

名字空间域

符号常量和inline函数可以被定义多次: 为了使编译器能够用一个常量值替换它的名字, 该常量的定义它的初始值必须在它被使用的文件中可见, 因为这个原因符号常量可以在同一程序的不同文件中被定义多次.

常量指针,指针常量,指向常量的常指针

常量指针

定义为指针指向的变量的值不可通过该指针修改, const在 * 前面 const int*p ( int const * p )

int main(int argc, char *argv[]) {int a=12; int const * p=&a; // or : const int *p=&a; cout<<*p<

指针常量

指针指向的数值可以改变,然而指针所保存的地址却不可以改变。 int* const p

int main(int argc, char *argv[]) {int a=12,b=13; int * const p=&a; //指针常量 cout<<*p<

指向常量的常指针

指针指向的地址和数值都是不可更改的。 const int const*p

int main(int argc, char *argv[]) {int a=12,b=13; const int* const p = &a; cout<<*p<

内联扩展

是用来消除函数调用时的时间开销。它通常用于频繁执行的函数。 一个小内存空间的函数非常受益。如果没有内联函数,编译器可以决定哪些函数内联。 自动对象的存储分配发生在定义它的函数被调用时. 因为与自动对象相关联的存储区在函数结束时被释放, 所以应该小心使用自动对象的地址, 自动对象的地址不应该被用作函数的返回值. 因为函数一旦结束了该地址就指向一个无效的存储区. 例如:

#include "Matrix.h"Matrix* trouble( Matrix *pm ){{Matrix res;// 用pm 做一些事情// 把结果赋值给resreturn &res; // 糟糕!}int main(){Matrix m1;// ...Matrix *mainResult = trouble( &m1 );// ...

< 在本例中该地址可能仍然有效, 因为我们还没有调用其他函数覆盖掉trouble()函数的活动记录的部分或全部, 所以这样的错误很难检测.>

new 表达式

没有返回实际分配的对象, 而是返回指向该对象的指针。对该对象的全部操作都要通过这个指针间接完成. 例如: int *pi = new int;

空闲存储区的第二个特点是分配的内存是未初始化的. 空闲存储区的内存包含随机的位模式. 它是程序运行前该内存上次被使用留下的结果. 测试: if ( *pi == 0 ) 总是错误的。除非:int *pi = new int( 0 ); 如果new 表达式调用的new()操作符不能得到要求的内存通常会抛出一个bad_alloc 异常 内存被释放–> delete pi; delete 表达式调用库操作符delete(), 把内存还给空闲存储区, 因为空闲存储区是有限的资源. 所以当我们不再需要已分配的内存时就应该马上将其返还给空闲存储区. 这是很重要的. 当程序运行期间遇到delete 表达式时, pi指向的内存就被释放了, 但是指针pi 的内存及其内容并没有受delete 表达式的影响. 在delete表达式之后pi 被称作空悬指针,即指向无效内存的指针, 一个比较好的办法是在指针指向的对象被释放后将该指针设置为0.

野指针

它没有被正确的初始化,于是指向一个随机的内存地址

int main(){ int *a=(int *)malloc(sizeof(int)); *a=1; printf("a:%d a'addr:%p\n",*a,a); delete(a); printf("a:%d a'addr:%p\n",*a,a); //A dangling pointer 空悬指针,原来所指的内存释放了 int *b; //A wild pointer 野指针 ,一开始 就没有赋初值 printf("b:%d b'addr:%p\n",*b,b); return 0;}/*a:1 a'addr:003E2460a:0 a'addr:003E2460b:-1869573949 b'addr:7C93154C*/

auto_ptr 对象被初始化为指向由new 表达式创建的动态分配对象。 当auto_ptr 对象的生命期结束时动态分配的对象被自动释放。 定义pstr_auto 时它知道自己对初始化字符串拥有所有权, 并且有责任删除该字符串。这是所有权授予auto_ptr 对象的责任。相关的头文件:#include string *pstr_type = new string( “Brontosaurus” ); 等价于:auto_ptr< string > pstr_auto( new string( “Brontosaurus” ) ); 两个指针都持有程序空闲存储区内的字符串地址,我们必须小心地将delete 表达式只应用在一个指针上。即有两个指针指向同一个由new创造的地址,用delete释放一次就可以了。 当一个auto_ptr 对象被用另一个auto_ptr 对象初始化或赋值时,左边被赋值或初始化的对象就拥有了空闲存储区内底层对象的所有权,而右边的auto_ptr 对象则撤消所有责任。 auto_ptr< int > p_auto_int; 因为p_auto_int 没有被初始化指向一个对象。所以它的内部指针值被设置为0,这意味着对它解除引用会使程序出现未定义的行为。 操作get()返回auto_ptr 对象内部的底层指针。所以为了判断auto_ptr 对象是否指向一个对象我们可以如下编程 if ( p_auto_int.get() != 0 &&*p_auto_int != 1024 ) *p_auto_int = 1024; 如果它没有指向一个对象,那么怎样使其指向一个呢——即怎样设置一个auto_ptr 对象的底层指针。我们可以用reset()操作例如 else // ok, 让我们设置 p_auto_int 的底层指针 p_auto_int.reset( new int( 1024 ) );

auto_ptr< string >pstr_auto( new string( “Brontosaurus” ) ); // 在重置之前删除对象 Brontosaurus: pstr_auto.reset( new string( “Long -neck” ) ); //在这种情况下用字符串操作assign()对原有的字符串对象重新赋值比删除原有的字符率对象并重新分配第二个字符串对象更为有效: pstr_auto->assign( “Long-neck” ); 不能用一个指向内存不是通过应用new 表达式分配的指针来初始化或赋值auto_ptr。 release()不仅像get()操作一样返回底层对象的地址而且还释放这对象的所有权:auto_ptr< string > pstr_auto2( pstr_auto.release() );

// 分配单个int 型的对象,用 1024 初始化 int *pi = new int( 1024 ); // 分配一个含有1024 个元素的数组,未被初始化 int *pia = new int[ 1024 ];

动态数组的建立:

对于用new 表达式分配的数组只有第一维可以用运行时刻计算的表达式来指定,其他维必须是在编译时刻已知的常量值。

#include #include int main(int argc, char *argv[]) { puts("please enter height, width, and length:"); int height=10,width=10,length=10; scanf("%d%d%d",&height,&width,&length); int ***a=new int**[height]; for(int i=0;i

用来释放数组的delete 表达式形式如下 delete [] str1; 在空闲存储区创建的const 对象有一些特殊的属性:首先const 对象必须被初始化。如果省略了括号中的初始值就会产生编译错误。 const int p; // [Error] uninitialized const ‘p’ [-fpermissive]

const int m=12;int cmp(int m){ m=::m+m; return m;}int main(int argc, char *argv[]) { printf("%d\n",cmp(10));}

// —– SortLib.C —– namespace { void swap( double d1, double *d2 ) { / … */ } } 函数swap()只在文件SortLib.C 中可见,如果另一个文件也含有一个带有函数swap()定义的未命名名字空间,则该定义引入的是一个不同的函数函数。swap()存在两种定义但这并不是个错误。 因为它们是不同的未命名的名字空间的定义,局部于一个特定的文件不能跨越多个文本文件。

自定义命名空间

跨越多个文件: a.h:

namespace a{ void print(){ printf("here is hero_a\n"); } }

b.h:

namespace b{ void print(){ printf("here is hero_b\n"); } }

mian.cpp:

#include #include #include "a.h"#include "b.h" int main(int argc, char *argv[]) { a::print(); b::print(); } /*here is hero_ahere is hero_b*/

static:

被static修饰的是静态变量。 静态局部变量保存在全局数据区,而不是保存在栈中

int get(){ static int g; g++; return g;}int main(){ cout<

静态全局变量

九 重载函数

重载

重载函数集的合适体的选择: 候选函数–可行函数–最佳匹配函。 可行函数的参数个数与调用的实参表中的参数数目相同,或者可行函数的参数个数多一些,但是每个多出来的参数都要有相关的缺省实参。 可能的转换被分成三组:提升promotion, 标准转换standard conversion, 和用户定义的转换user defined conversions。 用户定义的转换由转换函数conversion function 来执行。它是类的一个成员函数允许一个类定义自己的标准转换。 函数转换被划分等级如下:精确匹配比提升好,提升比标准转换好,标准转换比用户定义的转换好。

五种标准转换

1 整值类型转换:从任何整值类型或枚举类型向其他整值类型的转换(不包括前面提升部分中列出的转换) 2 浮点转换:从任何浮点类型到其他浮点类型的转换(不包括前面提升部分中列出的转换) 3 浮点—整值转换:从任何浮点类型到任何整值类型或从任何整值类型到任何浮点类型的转换 4 指针转换:整数值0 到指针类型的转换和任何类型的指针到类型void*的转换 5 bool 转换:从任何整值类型浮点类型枚举类型或指针类型到bool 型的转换 所有的标准转换都被视为是等价的。例如从char 到unsigned char 的转换并不比从char到double 的转换优先级高,类型之间的接近程度不被考虑,即如果有两个可行函数要求对实参进行标准转换以便匹配各自参数的类型则该调用就是二义的。将被标记为编译错误。 函数指针不能用标准转换转换成类型void*。 0 可以被转换成任何指针类型。 实参是该引用的有效初始值(类型一样)则该实参是精确匹配,如果该实参不是引用的有效初始值则不匹配。

十 函数模版 (续读)

这一部分的内容没有读最重要的部分 函数模板提供一个用来自动生成各种类型函数实例的算法。 用预处理器的宏扩展设施,例如#define min(a,b) ((a) < (b) ? (a) : (b)),可以避免为所有的不同类型数据都设计一样算法的函数。 函数模板提供了一种机制,通过它我们可以保留函数定义和函数调用的语义。 在一个程序位置上封装了一段代码,确保在函数调用之前实参只被计算一次而无需像宏方案那样绕过C++的强类型检查。 min函数的模版:

十一 异常处理

throw 表达式可以抛出任何类型的对象, 必须定义可以被抛出的异常, 在C++中异常往往用类class 来实现。 try 块(try block) 必须包围能够抛出异常的语句,try 块以关键字try 开始,后面是花括号括起来的语句序列。在try 块之后是一组处理代码,被称为catch 子句。

十二 泛型算法

泛型算法

操作在多种容器类型上的算法。 比如find()函数,iterator迭代器,sort()函数。 ———————————————————————————————————————— int数组的find()算法:

#include #include using namespace std;int main(){ int search_value; int ia[ 6 ] = { 27, 210, 12, 47, 109, 83 }; cout << "enter search value: "; cin >> search_value; int *presult = find( &ia[0], &ia[6], search_value ); cout << "The value " << search_value<< ( presult == &ia[6]? " is not present" : " is present" )<< endl; return 0;}

————————————————————————————————————————- vector的find()算法:

#include #include #include using namespace std;int main(){ int search_value; int ia[ 6 ] = { 27, 210, 12, 47, 109, 83 }; vector vec( ia, ia+6 ); cout << "enter search value: "; cin >> search_value; vector::iterator presult; presult = find( vec.begin(), vec.end(), search_value ); cout << "The value " << search_value<< ( presult == vec.end()? " is not present" : " is present" ) << endl; return 0;}

——————————————————————————————————————————- list的find()算法

#include #include #include using namespace std;int main(){ int search_value; int ia[ 6 ] = { 27, 210, 12, 47, 109, 83 }; list ilist( ia, ia+6 ); cout << "enter search value: "; cin >> search_value; list::iterator presult; presult = find( ilist.begin(), ilist.end(), search_value ); cout << "The value " << search_value << ( presult == ilist.end() ? " is not present" : " is present" ) << endl; return 0;}

对容器的遍历:iterator. 指针的泛化。 预定义函数对象被分成算术关系和逻辑操作, 每个对象都是一个类模板, 其中操作数的类型被参数化. 为了使用它们我们必须包含#include 以降序排列容器: vector< string > svec; // … sort( svec.begin(), svec.end(), greater()); // 预定义的类模板greater 它调用底层元素类型的大于操作符

const 容器只能被绑定在const iterator 上这样的要求与const 指针只能指向const 数组的行为一样在两种情况下C++语言都努力保证const 容器的内容不会被改变

为支持泛型算法全集根据它们提供的操作集标准库定义了

五种iterator

InputIterator, OutputIterator, ForwardIterator, BldirectionalIterator 和RandomAccessIterator InputIterator 可以被用来读取容器中的元素, 但是不保证支持向容器的写入操作 OutputIterator 可以被用来向容器写入元素, 但是不保证支持读取容器的内容 ForwardIterator 可以被用来以某一个遍历方向向容器读或写 BidirectionalIterator 从两个方向读或写一个容器 RandomAccessIterator 除了支持BidirectionalIterator 所有的功能之外还提供了在常数时间内访问容器的任意位置的支持

元素范围概念有时称为左闭合区间通常写为[ first, last ) // 读作: 包含 first 以及到 但不包含 last 的所有元素

四个算术算法

#include adjacent_difference()、 accumulate()、 inner_product()和partial_sum()

查找算法

equal_range()、lower_bound()和upper_bound() (二分查找实现)

int main(){ int a[6]={4,10,13,14,19,26}; int *val=lower_bound(a,a+6,12); //first value >= goal cout<<*val< goal cout<<*val<

函数equal_range()返回first和last之间等于val的元素区间.返回值是一对迭代器。

排列组合算法

next_permutation(), prev_permutation()

int A[3]={1,2,3};void show(){ for(int i=0;i<3;i++){ printf("%d ",A[i]); } cout<

标签:数据
上一篇:python数据结构及部分语法笔记
下一篇:ANSI C (2) —— str系列函数
相关文章

 发表评论

暂时没有评论,来抢沙发吧~