C++基础

命名空间

主要是为了解决名称冲突的问题

引用

变量的别名

引用和指针的区别:

  1.指针是个变量,可以把它再赋值指向别的地址

  2.建立引用的时候必须初始化,并且绝不会再关联其他不同的变量。而指针可以一开始就赋值空,之后可以再改变指向其他地方。

由于指针也是变量,所以,可以有指针变量的引用,比如下面的看着有点诡异的代码

int* a = NULL;
    int*& ra = a;//表示int*的引用ra初始化为a
    int b = 9;
    ra = &b;//OK,ra是a的一个别名,是一个指针
  • void引用是不合法的

  void& a = 3;void只是在语法上相当于一个类型,本质上不是类型,没有任何一个变量或者引用的类型的是void

  • 不能建立引用的数组
int a[10] ={0};
int& ra[10] = a;//error
  • 没有引用的指针和引用的引用
int a ;
int& ra = a;
int& *p = &ra;//error启动定义一个引用的指针
  • 有空指针,但没有空引用哇

无符号数Unsigned

这个操蛋的地方在于不能出现一个负值,如果一个int 和一个 unsigned出现在一个表达式里面的话会出现意想不到的效果,比如这个int是一个负值的时候,就会自动转换为unsigned,也就是通常会变成一个很大的整数;在比如一个负的unsigned和一个正的int进行比较的话,unsigned也会自动转换成一个合法的unsigned。

#include <iostream>
using namespace std;

int main(){
    int i = 2;
    unsigned j = -1;
    if (i > j)
      cout << "is true" << endl;
    else
      cout << "is false" << endl;
    cout << "j = " << j << endl;
    return 0;
}

is false
j = 4294967295

所以一定切记,不要让unsigned成为一个负数

引用的参数传递

  • 传递引用给函数与传递指针的效果是一样的。都能改变变量的值
  • 用引用作为参数比使用指针有更清晰的语法
  • 使用引用作为参数返回值,给函数带来的意义

    1.函数只能返回一个值,如果要返回两个值怎么办?可以用引用给函数传递两个参数,然后由函数往目标中填入正确的值

    2.函数返回值时要返回一个值的副本,而用引用返回时不需要生成副本,所以提高了效率

    注意:如果返回不在作用域范围内的变量或者引用那就有问题了。这与返回一个局部作用域指针的性质一样严重,比如

指针

关于常量指针

int a = 1 , b = 2;

const int *p = &a;  //p是表示指向一个const int数,不过指向一个非const的int也是可以的

p = &b; //是可以指向别的对象的,这里的const是底层const

*p = 5;// error !!

其实可以这样理解,const int *p 就表示它要指向一个const int 而这个数是不能被改变的,所以理所应当,*p是不能变的,它不需要去关心那个数到底是不是const,虽然前面也说,p是可以指向非const的

而如果换成

int *const p = &a; //这里的p就是代表一个指向int的常量指针,这里的const称作顶层const,修饰的是p

p = &b; // error , p是const不能改变,所以p不能再指向其他地方了

*p = 434; // 可以,int是可以改变的

const int *const p = &a ; //当然这样就 *p 和p都不能改变了

constexpr

常量表达式。如果修饰的是指针的话,那么只跟指针有关,跟指针所指的对象无关,比如

int i = 3 , j = 22;

constexpr int *p = &i; //这里可以指向const 也可以指向非const

p = &j; // error

*p = 4342; //可以

auto

可以让编译器替我们分析表达式所属的类型。

比如:

auto a = 4 + 2;
auto da = 4.3 + 3.3;
cout << "a = " << a << endl; //6
cout << "da = " << da << endl;//7.6

这个东西注意一个地方,auto能在一条语句中声明多个变量,但是这一条语句中的所有变量都必须同一个类型。

你不能这样搞:auto s = 0 , f = 3.3; // ERROR

string

1.string的size函数返回的不是一个int!!!!返回的是一个string::size_type类型的值。尽管不太清楚这个类型的细节,但是可以肯定:它是一个unsigned类型的值。所以通常用auto或者decltype来推断。比如统计一个string里面的标点符号

#include <iostream>
using namespace std;
int main(){
    string str = "Hello World!!!!";
    decltype(str.size()) punct_cnt = 0;
    for (auto c : str)
      if (ispunct(c))
        punct_cnt++;
    cout << punct_cnt << " punct in str" << endl;
    return 0;
}

4 punct in str

一个问题

#include <iostream>
#include <vector>
using namespace std;

int main(){
    unsigned scores[11];
    unsigned grade;
    while (cin >> grade)
      if (grade <= 100)
        ++scores[grade/10];
    for (auto i : scores)
      cout << i << endl;
    return 0;
}

为什么会有问题呢??? 为什么scores一定要初始化呢?scroes[11] = {};

强制转换

形式如下

cast-name<type>(expression); type是要转换的目标类型,expression是待转换的值。cast-name是static_cast、dynamic_cast、const_cast、reinterpret_cast中一中

const_cast

改变运算对象的底层const,将常量对象转换成非常量对象的行为。举个例子

#include <iostream>
using namespace std;

int main(){
    int i = 3;
    const int *ip = &i; 

    cout << *ip << endl;
    //*ip = 423;
    //cout << *ip << endl; //错误 *ip不能变
    int *p = const_cast<int*>(ip);
    *p = 23; 
    cout << *p << endl;   //23
    cout << "i = " << i << endl; // i = 23
    return 0;
}

上面本来是不能通过*p来改变对象的值的,但是const_cast等于是将const去掉了获取了写权限

要注意的是  i 本来就是可以改变的 ,如果把 i 改成const ,如:const int i = 3; 其他都不变,那么程序编译运行是没有问题的, *p也还是23 ,但是 i 不会变,还是3。

分离式编译

即允许我们把程序分割到几个文件中去,每个文件单独编译。程序复杂的时候,会希望把程序各个部分分别存储到不同的文件中。比如:

在同一个目录下,放了自己写的头文件 test.h ,还有 test.h 中声明的函数定义文件 fun.cpp , 以及放了main函数的 t.cpp

#include <iostream>
#include "test.h"

using namespace std;
int main(){
    cout << fun(4) << endl;
    return 0;
}
#include "test.h"

int fun(int i){ 
    return 2*i;
}
int fun(int i);

分离式编译的步骤如下:

g++ -c t.cpp fun.cpp

这里的-c参数是 Compile and assemble, but do not link(编译并汇编,但不链接),编译成功后会输出2个.o的文件,一个是t.o,一个是fun.o

然后再链接成一个可执行文件

g++ t.o fun.o -o t

后面这个 t 是代表要生成的可执行文件的名字,这个参数是必须的,不能省,完成后会生成一个名为t的可执行文件

原文地址:https://www.cnblogs.com/i-love-kobe/p/6057552.html