C++ Primer(第七 八章)

函数

  1. 函数定义
    • 函数调用符() 一对圆括号 操作数是函数名和一组实参 运算结果是函数的返回值 
  2. 参数传递
    • 非引用形参 均不改变实参的值;对于指针形参 不改变指针实参的值 但是可改变指针指向对象的值 如需保护指针指向对象 需要加const int *;
    • p201 关于编译器将const形参当做普通形参处理 以支持对c语言的兼容
    • 引用形参 改变实参 对比指针 引用更安全;当函数需要返回多个参数时,可以添加引用形参;避免将形参复制,使用于形参无法复制或者过大的场合;传递指向指针的引用;
    • const引用:
    • 数组形参:数组定义为形参时,int* int[] int [10] 三种表示是一样的;将数组当做形参,不要指定长度。因为编译器在检测实参时,只会检查实参是不是指针,指针类型和数组元素是否符合,不会检查数组长度;也可以用引用作为数组形参,但是需要int (&arr)[10]如此定义(记得用括号 否则优先级问题会导致问题),编译器会检查数组大小是否匹配;多维数组的传递:可以int matrix[][10]或者int (*matrix)[10](不可写为int *matrix[10]表示一个包含10个指向int的指针的数组),前者是根据多维数组定义,作为形参后会自动退化为指针,指向数组中的元素(即包含10个int数据的数组)后者是直接定义一个指针,指向包含10个int元素的数组;
    • 数组防止超出数组边界:在数组本身放置一个标志数组结束的标志;传递数组第一个元素和最后一个元素指向的下一个位置地址(容器中用得多);显示传递数组大小的形参;
    • main隐含形参处理命令行选项:int main(int argc,char *argv[]/char **){...} argc是字符串数组中参数个数,argv是字符串数组 每个字符串是一个参数 例如:prog-d -o ofile data0 表示执行prog文件中的main函数 argv[0]='prog'程序名 argv[1]='-d' argv[2]='-o'
    • void foo(...) void foo(parm_list,...) 省略号出表示对应实参可以强制停止类型检查
  3. return语句
    • return;return expression;return可强制退出函数;返回值为void一般用return,除非expression是另一个void返回值的函数;while循环里面有return返回时记得while之外也要有返回;main函数可以不用返回,其默认返回值为0 表示运行成功,否则有问题。也可以指定返回值表示特定含义;
    • 返回值可以是普通值或者引用值 返回普通值则会复制该值作为临时变量 返回引用值则不会复制 记得不能返回局部变量的引用 因为退出调用函数会释放该函数空间 局部变量也不存在了 同样也不能返回局部变量的指针;函数返回值为引用是 是左值,可以重新赋值,如果不希望赋值可以const限制;
    • 递归 一定要有停止条件

  4. 函数声明 

    • 函数在被调用时 先声明 后调用;函数声明和调用是可以分开的,只能定义一次 可声明多次;声明时也许返回类型 函数名 形参类型 但不需形参名 以上称为函数原型
    • 同样全局变量一样 函数也应在源文件中定义 在头文件中声明。使用处直接包含头文件即可,需要修改也只用改头文件即可 并保持在不同调用处的声明是一样的。
    • 默认实参 如果一个形参设置了默认值 那么它后面的形参也要设置默认值;默认实参只会用来替换函数调用缺少的尾部实参;函数的默认实参是根据当前声明函数形式来确定的,声明和定义时都可以设置默认实参,但都只在当前文件有效,并且只能一个文件 设置一次默认实参

  5. 局部对象

    • 名字的作用域:知道该名字的程序文本区 对象的生命期:程序执行过程中对象存在的时间;只有在函数被调用时才存在的对象称为自动对象;形参也是自动对象,在调用函数时创建,在函数结束时撤销;
    • 全局变量—局部变量// 静态变量—非静态变量// 静态变量(静态全局变量—静态局部变量)// 静态局部变量:该变量在函数作用域内但生命期跨越这个函数的多次调用,直到整个 程序执行完才撤销,但是作用域还是限于该函数内,不能再该函数外的地方使用该变量。  

  6. 内联函数

    • 一个短小的函数尤其好处又有坏处(运行时间比直接用表达式慢很多) 此时可见该函数设置为内联函数 适用短小 常用的函数 多数编译器不支持递归函数的内联
    • 内联函数在头文件中定义 在返回类型前加关键词inline  它的定义对编译器是可见的 且它的定义不止一次 因此包含在头文件中  可以保证每次定义形式完全相同
    • 修改了头文件中的内联函数 则需要将用了该头文件的所有源文件全部重新编译

  7. 类的成员函数

    • 函数原型同上所述 需要在类中定义 函数体可在类外;类内定义的函数隐式的当做内联函数;
    • 成员函数含有额外的 隐含的形参 将成员函数和调用该成员函数的类对象绑定在一起 this指针初始化为调用函数的对象的地址 不需要显示做为形参或者来访问被调函数的所述对象的成员
    • const成员函数 在形参的圆括号后添加const表示 给成员函数不能修改该函数的对象 只能读取对象的数据 注意::const对象 指向const对象的指针或引用只能用于调用其const成员函数 不能调用非const成员函数
    • 在类外定义函数时 需要在函数名前加类名和作用域操作符
    • 构造函数:构造函数与类同名 但没有返回类型 用来给成员变量初始化 可有多个构造函数;构造函数是在public中定义的; 可以显示定义一个没有形参的默认构造函数,可在圆括号后加冒号 和初始化列表进行默认初始化;系统可以自动添加默认构造函数 但一般适用于仅包含类类型成员的类 对于包含内置类型或者符合类型的成员类 不合适 需自己定义 (内置类型在局部作用域没有默认初始值)
    • 类代码文件的组织 将类的声明放在头文件中 类外定义的成员函数放在源文件中 习惯用简单的规则给头文件及其关联的类定义代码命名 类A 其定义文件名为A.h 成员函数文件名为A.cpp并包含头文件A.h

  8. 重载函数

    • 出现在相同作用域的两个函数 有相同名字而形参列表不同(形参类型不同 或者形参个数不一) 则称为重载函数;不能基于返回类型不同而称作重载;main不能重载;形参const或非const 带有默认实参等只能算作不同的声明 并不是重载 但是const引用形参和非const引用形参是不同的 同理const指针形参和非const指针形参也不同;不要过度使用重载函数,容易使得函数名和功能看起来不明确;
    • 一般的作用域规则也适用于重载函数名 局部声明的名字会屏蔽全局作用域的同名名字 ,因此每个版本的重载函数都应在同一作用域中声明。一般来说函数声明建议放在头文件 但为了较好理解作用域和重载的相互关系 将使用局部函数声明??
    • 重载函数:编译器先进性函数名匹配得到候选函数 然后实参对形参匹配选择可行函数 最后寻找最佳匹配函数(基于怎样的原则 尤其是在有多个形参的函数重载时 但如果选不出时 则具有二义性 会编译出错 此时需要显示的强制类型转换)
    • 优先级别:精确匹配;类型提升实现匹配(char 到int double依次级别提升);标准转换实现匹配;类类型转换实现匹配;属于同一优先级别,则具有二义性;对于枚举类型作为形参,即使实参是相同值的整数也是不能进行匹配的,但可以将枚举类型传给整数形参的函数作为实参。
    • 对于引用和指针形参:const和非const属于重载,const对象只可传给const引用形参,非const对象既可传给const形参引用 也可传给非const形参引用 但是传给非const形参引用显然级别更优先;对于指针 const指针和非const指针也属于重载,这里的const指针是指const修饰值 而不是const修饰的指针 同理,const对象只可传给const指针形参 非const对象均可。注意下面:
    • 1 f(int*);//1
      2 f(int *const);//2const修饰指针 不属于重载 传参数时1和2均复制了指针 但指针的const对指向的值的操作并没有影响 均可以操作
      3 f(const int *);//3const修饰值 属于重载 传参数时也会复制指针 但是和1比较 他是不可以通过指针修改值的 而1可以

  9. 函数指针

    • 函数指针指向特定的类型的函数(这里的类型是由返回类型和形参表决定的 而与函数名无关  指针的类型是由只返回类型和形参表类型 某个函数名可以用来初始化该指针);因此在使用函数指针的时候,一般先定义某种类型的函数指针,然后把它当作一种类型进行变量定义和赋值。例:bool (*pf) (const string &,const string &) (*pf)此处圆括号不能少 不然就定义为一个返回bool *的函数了
    • 可以用tpye简化函数指针的定义:typedef bool (*pf) (const string &,const string &) 后面可以直接把pf作为一种函数指针类型 可用它定义变量 将函数名赋值给变量即可,因为直接引用函数名就相当于函数名取地址 类似数组名一样;对函数指针赋值只能是零 或者同类型的函数指针和或者函数:pf p = FuncName;函数指针可直接调用该函数 可不需要解引用;
    • 函数形参可以是函数指针;函数返回值也可以是函数指针;对于重载函数,将函数名给函数指针的时候,会根据函数指针类型类进行匹配
    •  1 typedef bool (*cmpFcn)(const string &,const string &);
       2 bool lengthcompare(const string &,const sring &);
       3 bool cstringCompare(char*,char*);
       4 
       5 cmpFcn pf1 = 0;
       6 pf1 = lengthcompare;
       7 cmpFcn pf2 = &lengthcompare;
       8 cmpFcn pf3 = cstringcompare; //error
       9 
      10 pf("hi","bye");
      11 *pf("hi","bye");
      12 lengthcompare("hi","bye");
      13 
      14 void useBigger(const string&,const string&, bool (*) (const string&,const string& )); //第三个参数表示指向某个函数的指针 (*)可以忽略
      15 bool (*ff(int)) (const string &,const string &);//ff是一个函数名 形参是int 返回值是bool(*)(const string &,const string &)类型的函数指针,从里往外理解 
      16 cmpFcn ff(int);//根据以上type可以简化 cmpFcn就是返回类型 
      17 
      18 //函数名可以作为形参为函数指针的实参 此时函数名可以转换为函数指针 但是不能作为返回类型为函数指针的返回类型 因为返回的函数名无法转换为函数指针  

标准IO库 

    • istream 输入流类型    ostream 输出流类型    
    • cin 读入标准输入的istream对象  cout 写到标准输出的ostream对象  cerr 输出标准错误的ostream对象    
    • << 操作符 从istream对象读入    >> 操作符 把输出写到ostream对象中
    • getline 将istream和string类型的对象作为形参 通过istream读取单词到string对象中 
  1. 面向对象的标准库
    • IO读写 不仅要是用于用户控制窗口的交互 还可读写文件 格式化内存中的数据;IO读写为了支持不同设备和不同大小字符流 使用继承定义了一组IO类;基类和派生类之间的关系图如下:iostream读写控制窗口 fstream读写文件 stringstream读写string对象  i读入 o写到
    • 宽字符:wchar_t是C/C++的字符类型,是一种扩展的存储方式,wchar_t类型主要用在国际化程序的实现中,但它不等同于uni编码。uni编码的字符一般以wchar_t类型存储。char是8位字符类型,最多只能包含256种字符,许多外文字符集所含的字符数目超过256个,char型无法表示。wchar_t数据类型一般为16位或32位,wchar_t所能表示的字符数远超char型。
    • IO标准库为了支持wchar_t 每个类都有对应的支持该字符版本 wostream wiostream wiftream wcin wcout等等
    • IO标准库对象不允许赋值或者赋值操作  所以它不可能存储在vector等容器中(只有可以复制的元素类型可以存储在vector中) ;形参和返回类型不能是流类型, 如果需要传递或者返回IO对象 需要使用引用或者指针 以防止发生复制行为。 

  2. 条件状态

    • 如何掌管缓冲区及其流状态的相关内容:io标准库管理一系列的条件状态成员,用来标记给定的IO对象是否处于可用状态或者遇到哪种特定任务
    • 一般检查流有效即可(while(cin>>word)),有时还需要具体的状态,下图是标准库的条件状态定义:流对象包含一个iostate条件状态成员和三个iostate类型的常量值表示特定的位模式。流的状态有四种bad fail eof good 并且对应四个函数进行判断。可对条件成员状态整体或单个进行设置和恢复,对应三个函数。可返回条件状态。 

      strm::iostate

      机器相关的整型名,由各个iostream类定义,用于定义条件状态

      strm::badbit

      strm::iostate类型的值,用于指出被破坏的流[1,Linux32位] 无法修正

      strm::failbit

      strm::iostate类型的值,用于指出失败的IO操作[4] 可修正

      strm::eofbit

      strm::iostate类型的值,用于指出流已经到达文件结束符[2]

      s.eof()

      如果设置了流 s的 eofbit值,则该函数返回true

      s.fail()

      如果设置了流 s的 failbit值,则该函数返回true

      s.bad()

      如果设置了流 s的 badbit值,则该函数返回true

      s.good()

      如果流 s处于有效状态,则该函数返回true

      s.clear()

      将流 s中的所有状态值都重设为有效状态

      s.clear(flag)

      将流 s中的某个指定条件状态设置为有效。flag的类型是strm::iostate

      s.setstate(flag)

      给流 s添加指定条件。flag的类型是strm::iostate

      s.rdstate()

      返回流 s的当前条件,返回值类型为strm::iostate

  3. 输出缓冲区的管理

    • 每个IO对象会管理一个缓冲区 用于存储程序读写数据
    • 以下情况缓冲区刷新,即写入到设备或者文件:程序正常结束;缓冲区已满;操纵符显示刷新(flush等);用unitbuf设置流的内部状态对缓存区清空;将输入输出流进行关联 即共用缓冲区,当读入时会先刷新关联的缓冲区,交互式系统一般都应该如此设置
    • 最好多用endl刷新而非\n 程序出错非正常结束 不会刷新缓冲区 而进行调试的时候 可能因为是数据在缓冲区没有显示出来 而导致不便找到出错的地方 
       1 cout<<"hi"<<flush;//flush只刷新不添加
       2 cout<<"hi"<<ends;//ends刷新并添加null字符 
       3 cout<<"hi"<<endl;//endl输出换行符 并刷新缓存区  
       4 
       5 cout<<unitbuf<<"hi"<<"bye"<<nounitbuf;//用unitbuf设置后面每次输出都刷新 用nounitbuf关闭这种功能
       6 cout<<"hi"<<flush<<"bye"<<flush;//功能同上
       7 
       8 cin.tie(&cout);//一个istream或者ostream对象均可调用tie函数 其形参为一个指向ostream对象的指针 绑定后 实参和调用函数的对象建立关联 每次调用对象时 都会先刷新实参对象缓冲区
       9 ostream *old_tie = cin.tie();
      10 cin.tie(0);//打破cin和cout之间的关联
      11 cin.tie(&cerr);//建立cin和cerr之间的关联 不建议
      12 cin.tie(0);
      13 cin.tie(old_tie);//恢复cin和cout之间的关联 每次cin有读入时 需要先刷新cout的缓存内容

  4. 文件的输入输出

    • fstream的读写操作是对文件的操作 ifstream读文件的内容 ofstream写文件的内容 继承大部分iostream的内容 定义了open和close函数 以及形参为要打开的文件名的构造函数
    • 我们使用的cin cout cerr是标准库定义的IO对象 因此*fstream也需要定义对象,并将它捆绑在对应的文件上
    • 文件模式:文件模式是整型常量 在打开文件时 用位操作符设置一个或多个  通常同open和文件名构造函数打开文件有默认的打开模式参数  根据文件流的不同默认参数不同 如ifstream默认以in模式 ofstream默认out打开 会清空原文件内容  
    • in(打开文件读) out(打开文件写) app(每次写之前找到文件尾 追加) ate(打开文件后立即定位到文件尾 清空原文件内容) trunc(打开文件时清空已存在的文件流) binary(以二进制模式进行IO操作)
    • out app trunc只用于写   in只用于读   ate只在打开时有效   binary将文件按照字节序列处理 不解释流中的字符  模式可以组合但有些组合事无效的 out、out|app、out|trunc、in、in+out、in|out|trunc这些组合是有效的
    •  1 ifstream infile(ifile.c_str());//ifile ofile分别是保存文件名的string对象
       2 ofstream onfile(ofile.c_str());//IO标准库使用C风格字符串 先保存在string对象 然后用c_str转换
       3 
       4 ifstream infile;//定义了对象 但没有绑定文件 使用前 需要先绑定文件
       5 ofstream onfile;
       6 infile.open("in");//绑定文件
       7 onfile.open("out");
       8 infile.close();//解开绑定 关闭文件 以便重新绑定
       9 infile.open("in2");
      10 
      11 if(!infile)//检查文件是否打开
      12    {
      13        cerr<<"error"<<endl;
      14        return -1;      
      15 }
      16 if(outfile)//ok to use 检查文件是否可以写入内容
      17 
      18 ifstream input;//对象在循环外建立  循环内的所有文件是用一个流对象来处理的  需要不断open close ,并且clear文件流对前一个文件读取状态 
      19 vector<string>::const_iterator it = files.begin();
      20 while(it != files.end)
      21 {
      22     input.open(it.c_str);
      23     if(!input)  break;
      24     while(input>>s) process(s);
      25     input.close();
      26     input.clear();//如果在process时出现读取文件到结束或者中间出错 input的状态会保持在出错或结束 而无法进行下一个文件读取 因此需要清除文件流的状态
      27     it++;
      28 }              
       1 ofstream outfile("file1");//默认和下面的模式是同样的 out 并且trunc 清空并写
       2 ofstream outfile2("file1",ostream::out|ofstream::trunc);//按位或 同时取两种模式
       3 ofstream appfile("file2",ofstream::app);//以最追加模式写 不清空
       4 
       5 fstream inout("copyout",fstream::in| fstream::out);//fstream即可in又可out模式 默认同时打开 此时不清空;当指定out 会清空;当指定了trunc 即使指定了in 也会清空
       6 
       7 ofstream outfile;//模式是文件的 不是流的 因此流在绑定不同文件时可重新指定模式 
       8 outfile.open("file1",ofstream::out);
       9 outfile.close;
      10 outfile.open("file2",ofstream::out|ofstream::app);
      //opens in binding it to the given file 打开并检查输入文件
      ifstream& open_file(ifstream &in,const string &file)
      {
          in.close();
          in.clear();
          in.open(file.c_str);//打开文件 如果失败器条件状态标记出错 否则成功打开
          return in;
      }

  5. 字符串流

    • stringstream继承与iostream,很多操作相同;stringstream读写操作是对string字符串的操作,该类定义了成员函数str用来操作字符串;且定义有string为形参的构造函数;
    • stringstream对象的使用 转换/格式化(将内存中多种数据结构之间实现自动格式化 可转为string 也有由string转回其他数据格式)
    •  1 string line,word;
       2 while(getline(cin,line))
       3 {
       4     istringstream str(line);//将一行字符写入str
       5     while(str>>word)//依次读str中的每个单词
       6     {
       7         //do something
       8     }      
       9 }      
      10 11 int val1=512,val2=1024;
      12 ostringstream ostr;
      13 ostr<<"val1: "<<val1<<"\n"<<"val2: "<<val2<<"\n";//将数值转换为字符 并将这一串内容写到ostr
      14 
      15 istringstream istr(ostr.str());
      16 string dump;
      17 istr>>dump>>val1>>dump>>val2;//将字符转换为数值 将istr内容分解读入 “val1:”   512  “val2:”  1024忽略空白符 val1 和val2分别就是512和1024
      18 cout<<val1<<" "<<val2<<endl; //512 1024

       

 

posted @ 2015-01-21 09:11  oudan  阅读(216)  评论(0)    收藏  举报