C++ 进阶知识点(1)读取流 无符号类型 常量表达式 可变函数参数

1.在iostream头文件中除了经常使用的std::cout std::endl外其他也比较常用的函数

(1)std::cin用于从控制台读入数据

int x;

std::cin>>x;

std::cout<<x<<std::endl;

(2)std::cerr和std::clog

std::cerr用于输出警告和错误信息,和std::cout的区别是:它的输出内容是不缓存的,并且不能使用">"重定向输出到文件中

std::clog用于输出程序运行时的一般信息,用法和std::cout基本没有区别

2.在代码中我们用得比较多的是使用:执行程序 > 文件名的方式将原本应该输入到屏幕的数据重定向输入到一个我们指定的文件中

也可以使用: 执行程序 < 文件名的方式来将文件中的数据作为cin的输入读取到程序中哟。

3.std::cin的返回值

下面这个代码让用户一直输入一个整数并打印出来:

int x;
    while(std::cin>>x){
         std::cout<<x<<std::endl;
    }
这里使用了std::cin>>x表达式的值作为while的判断条件,那么std::cin>>x的返回值根据什么定义的呢?
>>输入运算符和<<输出运算符都是将其左侧运算对象作为计算结果,应该是一个istream的对象。
使用这个返回的istream对象作为while的条件时,效果就是监测流的状态如果是一个有效的流则为true,无效的输入或者遇到文件结束符时就为false,所以其实这里做了隐式转换。
当输入一个正常的整数时候就会让你继续输入,但是如果是一个字母时则while中判断为false跳出循环执行之后的代码。
或者遇到文件结束符时也为false,文件结束符在windows中是ctrl+z在liunx中是ctrl+d。
特别注意如果你输入是一个小数如1.5则程序会当做是多次输入第一次为1第二次为.第三次为5,这里在第二次时就会判断为false所以结果就是只打印一个1,并不是将1.5转换为了int类型的数据哟,同样的如果输入5a也是一样会打印出5

3.1.getline的使用,头文件#include <string>

getline和cin一样同样是一个输入流用于读取输入的数据,和cin不同的是它每次是读取一行即要到换行符为止,而cin遇到空格也会看成是读取的结束

std::string str;
   while(std::getline(std::cin,str)){
       std::cout<<str<<std::endl;
   }
std::string str;
   while(std::cin>>str){
       std::cout<<str<<std::endl;
   }
上面两个代码同样输入:this is a test。前者只输出一次this is a test,后者会输入四次分别是this,is,a,test
 
 

4.使用多行注释/**/的时候最好在每一行前面都加上*号如下:

/*this is a test

 *test add

 *test plus*/

5.类class和结构体struct的唯一区别就是,在书写其中的成员的时候如果最开始没有写任何的访问修饰符(public或者privite等),在clsss中默认时private,在struct中默认时public

6.在使用浮点数进行计算的时候尽量使用double,少使用float。因为float很可能会因为精度不够而导致数据丢失,而double双精度的运算速度和使用float单精度的运算速度相差无几。

7.在将一个小数隐式或者显示转化为一个int的时候并不是采取的四舍五入而是直接使用取整。如果想要使用四舍五入,可以使用std::round(double);,需要包含头文件#include <cmath>

8.在代码中如果将一个无符号类型的变量和一个有符号类型的变量进行运算的时候,机器会将这个有符号的变量类型隐式转化为无符号的。如果这个无符号的变量恰好是一个负数,那么计算的结果不可预期。

unsigned int x = 6;
    int y = -10;
    std::cout<<x +y<<std::endl;
 
9.我们把形如5、5.5、‘c’、0x14(十六进制)这样的值叫做字面值常量,每一个字面值常量对对应一个他们自己的数据类型,一般是长度能装下他们的该类型的最短类型。
可以给字面值常量加上一些特定的前缀或者后缀来显示的指定字面值常量的类型,如:54UL(表示54是一个unsigned long类型的无符号长整型),L'a'(表示‘a’是一个wchar_t类型的宽字符)
 
10.在函数外定义内置类型变量没有初始化其值为0,在函数内部定义的内置类型变量没有初始化其值不确定
void test(){
    int x;
    std::cout<<x<<std::endl;
}
int main(){
    int a;
    std::cout<<a<<std::endl;
    test();
}
 
11.常量表达式指的是值不会改变并且在编译过程中就能得到计算结果的表达式。字面值属于常量表达式。
const int a = 20;//a是一个常量表达式
const int b = a+10;//b是一个常量表达式
int c = 30;//c不是一个常量表达式,因为c的值是可以改变的
const int d = getSize();//d的值是一个常量不会改变,但是它的值在编辑阶段不能确定必须要在运行阶段才能拿到,所以不是一个常量表达式
在c++ 11中新增了constexpr来声明常量表达式和定义constexpr函数(返回值是一个常量表达式的函数)
 
12.string类型的size方法的返回值是一个string::size_type类型,而不是我们以为的int类型。size_type类型虽然和int类型接近都是肯定是一个整型,但是它肯定是无符号的。所以如果使用auto接收size()的返回值后一定不要再让其和负数一起运算。
之所以要额外定义一个size_type类型是为了实现机器无关的特性。
 
13.虽然我们经常使用std::string str="hello";这样的语句来为一个string对象赋值,但是“hello”这个字面值的类型并不是string。
我们使用decltype("a")你会发现它的返回类型是const char [2]。在使用std::string str="hello";这个语句时实际上做了一个隐式转换。
std::string str="hello" + "world";//这个语句编译器会报错,因为“hello”或者"world"它的字面值类型并不是string,没有重载+运算符。但是如果将其中一个改为一个string对象就是合法的。
 
14.当我们要判断一个char类型的字符的特性时,比如判断是数字还是字母还是控制字符之类的。可以使用#include <cctype>头文件中定义的一些方法
<cctype>头文件就是c语言中ctype.h的c++版本
 
15.在用下标访问string对象中某个指定的元素时,这个下标的类型也是string::size_type
std::string str= "hello";
int a = 2;
char c = str[a];//这里其实作为一个隐式转换将int类型的a转化为了string::size_type类型
 
16.在初始化一个数组的时候也可以使用列表初始化,并且使用列表初始化数组的时候允许省略掉数组的长度:
int a[5]{1,2,3,4,5};
int b[]{1,2,3};
声明数组的时候,长度必须是一个常量表达式,即必须是在编译阶段就能得到其值。
int x = 9;
int b[x];//报错,因为x不是一个常量表达式
可以使用一个字符串字面值来为一个字符数组初始化,这样初始化的字符数组除了字符串字面值中的所有字符外还会在数组的末尾额外添加一个‘’,并且这样的初始化方式允许省略掉数组的长度声明:
char c[]="hello";
数组之间不允许进行相互赋值:
char c[]="hello";
char c1[]="heheh";
car c2 = c;//报错
c = c1;//报错
 
17.在有些情况下我们无法确定表达式的求值顺序,在这种情况下需要谨慎处理:
如int i = fo() * fo1();//这个表达式中我们并不清除会先执行fo()还是先执行fo1(),如果在这两个函数中都对某个同一对象进行了操作,那么调用顺序的不同很可能会导致表达式计算结果不一样。可以先手动调用两个函数再将其返回值带入表达式。
 
18.c++中有几种命名的强制类型转换,但感觉平时很少用到,就不仔细记录了,他们分别是:static_cast、const_cast、reinterpret_cast、dynamic_cast
 
19.一般的异常处理代码:
try{
}
catch(exception ex){
std::cout<<ex.what()<<std::endl;
}
 
20.声明一个没有参数的函数的时候,如:
void test(){std::cout<<"this is a test"<<std::endl;}
可以写成下面这种形式,显示的定义空形参:void test(void){std::cout<<"this is a test"<<std::endl;}
 
21.局部静态对象
在函数中,将变量声明为static类型后,该变量会在第一次调用函数的时候初始化,并且当函数运行结束后,该变量不会被销毁任然存在,直至整个程序结束才销毁该变量。
int test1(int x){
    static int m = 0;
    m=m+x;
    return m;
}
int main(){
    cout<<test1(5)<<endl;//输出5
    cout<<test1(5)<<endl;//输出10
}
 
22.向main函数中传参:
int main(int argc,char *argv[]){//第二个形参argv是一个char*(c风格的字符串)的数组,第一个形参argc表示argv数组的长度
    for(int i = 0;i<argc;i++){
        cout<<argv[i]<<endl;
    }
}
argv中第一个argv[0]保存的是程序的名字,后面的数据才是传入的参数,在运行程序时后面跟的每个参数之间用空格隔开:./test -o he -f。则argv[0]=./test argv[1]=-o argv[2]=he argv[3]=-f
 
23.可变函数参数的写法
(1)继承自c中省略号可变参数,头文件#include <cstdarg>:
void test3(int x,...){
    va_list valist;//声明接收可变参数的对象
    va_start(valist,x);//表示从哪一个参数后开始接收可变参数
    char* str = va_arg(valist,char*);//依次接收可变参数,需要指定参数的类型
    cout<<str<<endl;
    double y = va_arg(valist,double);//依次接收可变参数,需要指定参数的类型
    cout<<y<<endl;
    va_end(valist);//参数接收完毕
}
int main(int argc,char *argv[]){
    test3(5,"test",2.33);
}
(2)c++ 11中新增使用initializer_list声明可变参数函数,用法见v博文:c++ 11新特性
原文地址:https://www.cnblogs.com/maycpou/p/14481514.html