C++基础知识

1,命名空间

namespace +自定义名称{  };里面可以包含变量,函数,结构等等。当想要使用其中的变量时,会使用“::”称为:域作用限定符。

(1)如果加了域作用限定符,就会从指定的域中寻找,如果不加,则会全局进行寻找.

(2)可以在命名空间中再命名空间,这时如果想使用内部命名空间中的变量时可以采用xxx::x::rand(外部命名空间名称为xxx,内部命名空间名称为x,rand为定义在x中的变量)

(3)在命名空间外调用结构体时,写法稍有不同: struct  xxx::Node node;(命名空间的名称为xxx,结构体名称为Node)

(4)可以多次命名空间,如果命名空间的名称相同,命名空间就会合并

(5)在命名空间中不能定义俩个相同的变量,除非在命名空间内,再建立一个命名空间,这时,虽然外部命名空间与内部命名空间有一个名称相同的变量,但是他们的域不同,所以并不冲突

(6)展开命名空间的方式有三种:

1>全部展开(全部授权)using namespace xxx;(xxx为命名空间名称)

2>部分展开(部分授权)using xxx::add;(xxx为命名空间名称,add为命名空间内部的函数名称),之后就可以直接使用命名空间中的add函数了

3>展开指定变量,例如:xxx::add,每次使用add时,都要这样来展开

(7)using namespace std :std是c++标准库的命名空间


namespace world 
    int rand = 0;
    int add(int a, int b) {
        return a + b;
    }
    struct Node {
        struct Node* next;
        int val;
    };
    namespace xxx {
        int rand = 0;
    }
}
int main() {
    printf("%p\n", rand);//从全局中找
    printf("%p\n", world::rand);//“域作用限定符”,从规定的域中找
    printf("%d\n", world::add);//如果不加“world:: ”会从全局寻找,寻找不到
    printf("%d\n", world::xxx::rand);
    struct world::Node node;
}

2,缺省参数

缺省参数:是指在声明或定义函数时为函数的参数指定一个默认值

(1)分为全缺省和半缺省

//全缺省
void func1(int a = 1, int b = 2, int c = 3) {
    cout << a <<b<<c<< endl;
}
//半缺省
void func2(int a , int b = 2, int c = 3) {
    cout << a << b << c << endl;
}

(2)当函数有多个(n)参数,调用函数式,只传入少于多个参数(<n)时,采用从左到右一次传入,例如:func1(10,20);,func打印出来的为10 20 3.

(3)缺省参数的应用:

namespace xx {
    typedef struct Stack {
    int* a;
    int top;
    int capacity;
}ST;
void StackInit(ST* ps, int N = 4) {
    ps->a = (int*)malloc(sizeof(int) * 4);
    ps->top = 0;
    ps->capacity = 0;
}

ST st1;
StackInit(&st1, 10);
for (size_t i = 0;i < 10;i++) {
    StackInit(&st1, i);
}
ST st2;
StackInit(&st2, 10);
for (size_t i = 0;i < 100;i++) {
    StackInit(&st1, i);
}

可以根据所需空间的不同,开吧、开辟不同大小的空间
ST st3;
StackInit(&st3);//默认开辟4块空间

3,函数重载

函数重载:在同一作用域,函数名相同,参数不同(类型不同,个数不同,类型的顺序不同)
注:返回值不同不能构成重载!!

例如:

int func(int a,double b){……  }

int func(double b,int a){ …… }

int func(int a){……  }

int func(double b){ …… }

4,编译器处理的流程

Tst.cpp
//预处理:头文件展开,宏替换,去掉注释,条件编译(#ifdef),
//Test.i
//编译:检查语法,生成汇编代码(指令级代码)
//Test.s
//汇编:将汇编代码生成二进制的机器码
//Test.o
//链接:合并连接,生成可执行程序
//a,out/xx.exe

5,&

&:类型后面加&,表示引用变量名(底层逻辑与指针相似)

(1)int a = 0;
    int& b = a;相当于给a取了另一个名字
    cout << &a << endl;
    cout << &b << endl;   a,b指的同一块空间

(2)

int a = 0;
int& b = a;将a重命名
int x = 2;
b = x;对a/b进行赋值,并没有改变指向

(3)注意:

1,引用必须在定义的地方初始化
2,一个变量可以有多个引用
3,引用一旦引用一个实体,再不能引用其他实体

举例:
void swap(int& x, int& y) {//相当于这里直接对实参x,y进行操作
    int tmp = x;
    x = y;
    y = tmp;
}

swap(x, y);

(4)传引用返回

int& count() {
    int n = 0;
    n++;
    return n;
}//返回的值为n的别名,非常危险!!!(类似于野指针)

//int ret = count();//打印的结果可能是1,也可能是随机值,取决于栈帧清没清
//cout << ret << endl;
//cout << ret << endl;

//int& ret = count();
//cout << ret << endl;//1

//cout << ret << endl;//随机值

//cout是一个函数,函数调用先传参,先把ret的值传过去,然后再栈帧上开辟内存,可能会将ret的值覆盖掉,所以第二次打印为随机值

int& add(int a, int b) {
    int c=a + b;
    return c;
}

int& ret = add(1, 2);
add(3, 4);
cout << ret << endl;//7

原因与上述相似,ret指向 add(1, 2)结束后,回收的空间,但add(3, 4),将add(1, 2)开辟的空间重新覆盖,而那块ret指向的空间也被覆盖,重新利用,里面的值也发生改变(变为了3+4的值)所以打印ret=7

注意!!!:以上皆为传引用返回的错误示范,知道上述大致原因即可,书写代码时不要这样书写!!

(5)注意:

如果函数返回时,出了函数作用域,如果返回对象还在,可以使用引用返回。如果已经还给系统了,则必须使用传值返回!!!

传引用传参(任何时候都可以)
//1,提高效率(比如传入一个较大的数组,直接对数组本身进行修改,省去了多次拷贝的过程)
//2,输出型参数(形参的修改,影响实参)

传引用返回(出了函数作用域,对象还在)
//1,提高效率
//2,修改返回对象(例子如下:)

书写顺序表的时候,想要打印和修改时,是需要书写两个接口才能实现,但是如果使用传引用返回,则只需要书写一个接口就可以实现,

struct SeqList {
    int* a;
    int size;
    int capacity;
};

c的接口设置:
int SLAT(struct SeqList* ps, int i) {
    assert(i < ps->size);
    return ps->a[i];
}
int SLModify(struct SeqList* ps, int i,int x) {
    assert(i < ps->size);
    return ps->a[i]=x;
}

c++的接口:

int& SLAT(struct SeqList& ps, int i) {
    assert(i < ps.size);
    return ps.a[i];
 }

打印:cout<<SLAT(s, 0)<<endl;

修改:SLAT(s, 0) = 1(1为修改后的数值)

6,&使用规则

&(取别名时个规则)变量的权限可以平移,可以缩小,但是不能放大

权限缩小:int x = 0;
const int& y = x;(用const修饰变为成变量,不能进行修改)

权限平移:const int a = 0;
const int& c = a;

权限放大(报错):const int a = 0;
int& b = a;(报错)

举例:

int i = 0;
double& d = i;(报错!!)因为:这里i会先复制一份,然后再将复制的一份拷贝给d,而复制的这一份是临时变量,临时变量具有常性,所以这里相当于权限放大(注意:赋值拷贝不受上述规则的限制)
const double& d = i;//权限没有放大

int ret = func();//返回时,拷贝一份(临时变量),赋给ret
const int& ret = func();(加const的原因与上述例子一致)

7,指针和引用的区别

(1)引用概念是定义一个变量的别名,而指针储存的是一个地址

(2)引用必须初始化,指针没有要求

(3)没有null引用,但有空指针

(4)sizeof的含义不同,引用为引用类型的大小,指针是地址空间所占字节数

(5)有多级指针,但没有多级引用

(6)引用比指针使用起来更加安全

8,宏函数

例如:

#define ADD(x,y)((x)+(y))

注意x,y要单独加括号,来确保运算的优先级。其次,这条语句后面没有‘ ;’。

ADD(a|b,a&b)如果x,y没有单独加括号,那么这个式子等同于(a|b+a&b),这时会先计算b+a

缺点:

1,容易出错,语法坑很多

2,不能调试

3,没有类型安全检查

优点:

1,没有类型的严格限制

2,针对频繁调研的小函数,不需要在建立栈帧

9,内联函数(inline)

inline int add(int x.int y ){return x+y;}

优点:不会建立栈帧

一般建议函数规模较小,不是递归且不频繁调用的函数用inline修饰,编译器会忽略内敛请求,内联只是向编译器发出一个请求,编译器可以选择忽略这个请求

如果大规模的函数运用内联,会产生代码膨胀。

当内联函数声明和定义在一个文件里,可以调用,但要注意,声明加inline,定义不需要再加inline。如果只有声明,则不能调用,因为声明和定义分离

内联函数不能声明定义,分离。否则会出现连接错误。因为内联函数,在调用的地方展开,不会建立栈帧,不会生成函数的地址,就不会进入符号表。

因此内联函数可以放到头文件里。

10,auto

auto 可以自动推导类型

普通场景没有价值,类型很长的时候就有价值,可以简化代码,不能定义数组,不能去做参数,也不能作为返回值

typeid(变量).name()=>可以查看变量的类型

11,范围for:遍历数组

for(auto e:array){

cout<<e<<" ";}

for(auto& e:array){

x*=2}//可以将数组中的数据改为二倍

  • 5
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值