文章目录
一些c语言基础
命名空间
定义
namespace是单独的作用域 两者不会相互干涉
namespace 名字
{
//变量 函数 等等
}
eg
namespace nameA {
int num;
void func() {
cout << "nameA";
}
}
namespace nameB {
int num;
void func() {
cout << "nameB";
}
}
使用
::作用域操作符, 空间名::成员
cout << nameA::num<< "==" << nameB::num << endl;
注意
- 命名空间只能再全局范围内定义
- 可以嵌套定义
namespace nameA {
namespace A {
int num;
}
int num;
void func() {
cout << "nameA";
}
}
- 命名空间声明和实现可以分割开来
- 命名空间可以起别名
namespace studentInfoHandle {
int id, age;
string name;
void studying() {
cout << "i am studying" << endl;
}
}
// 别名
namespace sIH = studentInfoHandle;
引用
基本
引用可以看作一个已定义变量的别名
语法:Type&name=var;
注意:
- &在这里不是求地址运算,而是起标识作用
- 类型标识符是指目标变量的类型
- 必须在声明引用变量时进行初始化
- 引用初始化后不能改变(值可以变 引用不可变=》不可以引用其他的变量)
- 不能有NULL引用。必须确保引用是一块合法的存储单元的关联
//一改均改
int a = 50;
int& b = a;
b = 100;
//均是100
cout << a << endl;
cout << b << endl;
int& c = a;
c = 500;
//均是 500
cout << a << endl;
cout << b << endl;
cout << c << endl;
//地址 相同
cout << &a << endl;
cout << &b << endl;
cout << &c << endl;
本质
引用的本质是一个常指针,也就是指针常量
int * const p
引用和指针没有本质的区别
秘籍:
-
const在前,内容不能变;
-
const在后,指针不能变;
引用所占空间大小和指针相同
int &p=a; == int* const p=&a;
引用作为参数
typedef struct Teacher {
int id, age;
};
void changeT(Teacher& t) {
t.age = 100;
}
/*
编译后 两者的功能是一样的
void changeT(Teacher* const t) {
t.age = 100;
}
*/
Teacher t;
t.age = 50;
changeT(t); //会改变结构体t的age的值
cout << t.age << endl; //100
- 引用作为其他变量的别名存在 一些场合下可以替代指针
- 引用相比于指针有更好的可读性和实用性
引用的使用场景
对象中使用多 后续来看
引用和const
C++也对引用做了特别的处理。
如果引用的数据对象类型不匹配,当引用为const时,C++将创建临时变量,让引用指向临时变量 (这一点指针是做不到的)
void func(const int &x){
cout<<x<<endl;
}
func(5);//正确的
/*
const int &x = 8;
内部的处理
int tmp=8;
const int&x = tmp;
*/
将引用形参声明为const的理由:
- 使用const可以避免无意中修改数据
- 使用const使函数能够处理const和非const实参,否则将只能接受非const实参(常量)
- 使用const,函数能正确生成并使用临时变量
左值是可以被引用的数据对象,可以通过地址访问它们,例如: 变量、数组元素、结构体成员、用和解引用的指针。
非左值包括字面常量(用双引号包含的字符串除外)和包含多项的表达式。
内联函数
引出
宏实现简单函数
宏操作在预处理阶段就是简单的文本替换 没有类型检查
#define ADD(x,y) x+y
// 要300 则x+y加上括号
int res = ADD(20,10)*10; //希望是300 但是实际上是 20+10*10=120
因此引出了内联函数
内联函数是一个真正的函数,但是没有函数的调用开销,又像普通函数一样可以传参返回值
相比于宏:既保持了宏函数的效率,又增加了安全性。
基本概念
定义
inline void func(){}
c++规定 内联函数的声明和定义必须放在一起
注意
- 推荐使用内联函数替代宏代码片段
- 内联函数在最终生成的代码中是没有定义的,所有内联函数的作用域可以理解位只在定义的文件中。那个文件调用那个文件就要定义,不能跨文件访问
- 内联函数的内容会嵌入到主程序。速度快,占内存高
- inline只是对编译器的一个内敛请求,c++内敛编译会有一些限制,以下情况编译器可能考虑不将函数进行内敛编译:
- 存在任何形式的循环语句
- 存在过多的条件判断语句
- 函数体过于庞大
- 对函数进行取址操作
函数补充
默认参数
// 带有默认参数被声明了 实现得使用就不需要传入默认参数了
//声明
int func(int r,double PI=3.14);
//实现
int func(int r,double PI){}
int func(int r=1,int f); // 错误得
注意事项
- 默认参数后面得参数必须都是默认参数
- 带有默认参数函数被声明了,那么实现得使用就不需要传入默认参数了
函数重载
c中
void func(){}
void func(int x){}
//报错 不允许 认为是一个函数
c++中函数重载定义
同一个函数名定义不同的函数
函数名和不同的参数搭配时函数的含义不同
条件
- 作用域相同
- 参数的个数不同
- 参数的类型不同
- 参数的顺序不同
namespace function0 {
void fun() {cout << "func" << endl;}
void fun(int x) { cout << "func(int x)" << endl;}
void fun(int x,int y) { cout << "func(int x,int y)" << endl;}
void fun(int x,char y) { cout << "func(int x,char y)" << endl;}
void fun(char x, int y) { cout << "func(char x,int y)" << endl;}
}
function0::fun();
function0::fun(1);
function0::fun(1,2);
function0::fun(1, 'a');
function0::fun('a', 1);
函数重载的原理
- 编译器在将程序编译完成后会将变量和函数变成一个个的符号,存放这些符号的表格称为符号表
- 对程序进行编译查看对应函数的符号
# 编译命令
g++ -c main.cpp
# 执行命令 查看符号表
nm main.o
g++编译器在将函数转化为符号时,根据函数名、形参类型进行转化
如果使用g++编译c语言含义函数重载的代码,是编译成功的。
const详解
const int buf = 512;
编译器在编译阶段会把buf替换为512
默认情况,const对象仅在文件内有效
多个文件共享const对象要用extern修饰
const修饰普通变量
被修饰的对象是只读的
看const修饰谁的时候可以将变量类型删除,然后就近原则
const int a = 10; //只读的
// a=100; 这个是错误的
const int *p1 = &a; //*p1是只读的。指针p1不能改变他所指向的内存空间的内容
//*p=50; 错误的
int const *p2 = &a; //*p2是只读的 等价于const int *p1 = &a;
const int* const p3=&a;//p3是只读的,*p3也是只读的。p3指针初始化后不能指向别的空间,其空间的值也不能修改
int const* const p4 = &a; //等价于 const int* const p3=&a;
const修饰成员变量
const修饰成员变量,表示成员变量是只读的,不能被修改
只能在初始化列表中赋值
class I{
public:
const int x;
I():x(100){};
};
const修饰成员函数
使用const修饰的成员函数,该函数为类的常成员函数,不能改变对象的成员变量
成员变量使用mutable
修饰可以突破const的修饰
const放在 函数名称(形参列表)
的后面
增强代码可读性
class I{
public:
int x;
int getX() const{
// x=100; error: assignment of member 'I::x' in read-only object
return x;
}
};
注意:const不能修饰全局函数
const修饰对象
-
const修饰的对象是常量对象,其中任何变量都不可以被修改
-
const修饰的对象只能访问const修饰的成员函数
- 调用非const修饰的成员函数可能修改成员变量的值,这违背了const修饰对象的原则
-
const修饰的对象可以访问public成员变量,但是不能修改
class Y{
public:
int x;
Y():x(100){}
void get(){
std::cout<<x<<std::endl;
}
};
const修饰引用
//const修饰引用目的:不希望通过引用改变被引用对象的内容
// 和修饰形参指针一样
void func(const Y &y){
}
const修饰函数返回值
用的不多,含义和const修饰指针和普通变量的含义基本相同
//返回值为基本变量
const int fun(){
int x = 100;
return x;
}
//返回值是对象
const Y funY(){
Y y;
return y;
}
//返回值为指针类型
const char *func4(){
//之所以用const修饰 是希望调用函数的用户不能够修改这个函数体内在堆上申请的空间中的内容
//返回栈上的地址是无意义的,栈空间在函数调用后会被系统自动回收的
char *p = new char [10];
strcpy(p,"hello");
return p;
}
//返回值为对象的引用
const Y &fun5(Y &t){
return t;
}
//返回值为基本变量时 使用const修饰无作用
int x = fun(); // a=b 即int=const int 不能通过a将b的值修改,所以即使b为const也无作用
//等价 const int x = fun();
std::cout<<x<<std::endl;
//返回值是对象 使用const修饰无作用 不会通过左值修改右值
Y y = funY();
//等价 const Y y = funY();
//返回值是指针类型 必须使用 const char * 来接受
const char *p = func4(); //char*=const char*是错误的
//返回值是引用
Y yy= fun5(y);
//等价 const Y yy= fun5(y);
const Y &i = fun5(y); // 必须加const
extern “C” (掌握迷迷糊糊)
extern "C"含义
- extern是C/C++语言中表明函数和全局变量作用范围的关键字,该关键字告诉编译器,其声明的函数和变量可以在本模块或者其他模块中使用
- 通常,在模块的头文件中对本模块提供给其它模块引用的函数和全局变量以关键字extern声明。例如,如果模块B欲引用该模块A中定义的全局变量和函数时只需包含模块A的头文件即可。这样,模块B中调用模块A中的函数时,在编译阶段,模块B虽然找不到该函数,但是并不会报错,它会在链接阶段 中从模块A编译生成的目标代码中找到此函数。
- extern “C” 包含双重含义:首先,被它修饰的目标是外部的;其次,被它修饰的目标是”C“
extern "C"的作用
实现c和c++的混合编程(c-gcc c+±g++),两种方法:一种在原文件,一种在头文件
main.cpp
#include <iostream>
#include "example.h"
//第一种方法
//extern "C"{
//#include "example.h"
//}
int main() {
std::cout<<add(2,3);
return 0;
}
/*
* 此时 main.cpp 使用g++编译
* example.c 使用gcc编译
* 但是两者对函数生成的符号是不相同,所以不可以直接访问
* 那么我们就要使用到 extern "C"
* */
example.c
#include "example.h"
extern int add(int x,int y){
return x+y;
}
example.h
#ifndef C__C___EXAMPLE_H
#define C__C___EXAMPLE_H
#ifdef __cplusplus //第二种方法 __cplusplus 是g++编译器所特有的
extern "C"{ //只有g++编译器才能识别 gcc不认识
#endif
extern int add(int x,int y);
#ifdef __cplusplus
};
#endif
#endif //C__C___EXAMPLE_H
nullptr
C++中为了避免“野指针” (即指针在首次使用之前没有进行初始化)的出现,我们声明一个指针后最好马上对其进行初始化操作。如果暂时不明确该指针指向哪个变量,则需要赋予NULL值。除了NULL之外,C++11新标准中又引入了nullptr来声明一个“空指针”,这样,我们就有下面一种方法来获取一个“空指针” :
int *p1 = NULL;
int *p2 = nullptr;
int *p3 = 0;
为什么c++11引入nullptr?
1、NULL在C++中的定义,NULL在C++中被明确定义为整数0
2、NULL在C中通常被定义为
#define NULL ((void *)0)
当
int fun(int *p){}
int fun(int p){}
fun(NULL); //调用的是 int fun(int p){}