C++基础知识
C++关键字
C++命名时,不可使用关键字进行命名,否则系统直接报错,关键字是C++语言本身设定好的,有其各自的用途。C++中共包括63个关键字。
命名空间
定义
命名空间定义是所使用的关键字是
namespace //后边紧接的是所命名的空间的名字
例如在C++中,我们需要在开头添加
using namespace std;//空间的名字为std
又例如普通的命名空间
namespace A1
{
int a ;
int add(int b,int c)
{
return b+c;
}
}
命名空间的使用
我们定义完一个空间之后,有时需要使用到所命名空间中的成员,所以此时我们使用命名空间。
第一种(加命名空间名称及作用域限定符)
我们以定义中所举的普通命名空间为例子:
namespace A1
{
int a ;
int add(int b,int c)
{
return b+c;
}
}
int main()
{
printf("%d\n",A1::a);
return 0;
}
第二种(使用using将成员引入)
//
using A1::b;
int main()
{
printf("%d\n",A1::a);
printf("%d\n",b);
return 0;
}
第三种(使用using namespace引入)
//1、
include<iostream>
using namespace std;
//2、
using namespace N;
int main()
{
printf("%d\n",A1::a);
printf("%d\n",b);
return 0;
}
C++的输入和输出
在学习c语言时,我们所使用的输入和输出是scanf和printf。这两个函数都是包含在头文件
#include<stdio.h>
而在C++中,我们所使用的输入和输出分别是cin和cout,而这两个标准输入和输出需要包含一个头文件和一个命名空间,
#include<iostream>
using namespace std;
如下所示,打印输出Hello C++!
#include<iostream>
using namespace std;
int main()
{
cout <<"Hello C++!"<<endl;
return 0;
}
又例如输入实现两个整形相加的功能
#include<iostream>
using namespace std;
int main()
{
int a ;
int b ;
cin >> a ;
cin >> b ;
cout << "sum =" << a + b <<endl;
return 0;
}
运行结果如下图所示:
函数重载
重载的概念
函数重载就是说允许在同一作用域下使用多个同名的函数,但是这些函数需要满足一定的条件,满足以下三个条件即可实现函数的重载。
- 参数个数不同
- 参数的类型不同
- 参数的顺序不同
如下代码 即为函数的重载:
#include<iostream>
using namespace std;
int add(int a , int b )
{
return a + b ;
}
double add(double a , double b )
{
return a + b ;
}
long add(long a , long b )
{
return a + b ;
}
int main()
{
add(1,2);
add(1.0,2.0);
add(1L,2L);
return 0;
}
需要注意的一种情况是,如果函数参数书的个数、类型、顺序都相同,只有返回值不同的话,则不满足函数重载的条件,所以函数不会重载。如下代码所示:
#include<iostream>
using namespace std;
int add(int a , int b )
{
return a + b ;
}
long add(int a , int b )
{
return a + b ;
}
为什么C++中可以发生重载
在学习c语言是,当两个函数发生重名时,会直接报错,但是在C++中就可以发生函数的重载。
(仅个人理解)
这是因为在c语言中,函数名和地址会链接在一起,例如
int add(int a, int b)
{
return a + b;
}
在c语言中,如果创建了一个add的函数,会发生这种情况(地址 ),当创建第二个add函数时,两个同名函数会指向同一个地址,这时如果调用的话,编译器不知道所调用的是哪个函数。如果是在c++中,当创建了如下两个函数
int add(int a, int b)
{
return a + b;
}
double add(double a, double b)
{
return a + b;
}
虽然函数名字相同,但是c++在创立这两个函数时,并不是将函数名和地址绑定,而是(地址 <_Zxxxii>和地址<_Zxxxdd>)。
所以在c++中可以发生函数的重载,而c语言中不可以发生函数的重载。
缺省参数
简单地说,就是在定义函数后,可以为函数中所调用的参数设定默认值,如果没有设定默认值的话,直接传入此参数的实参。
如下列代码及运行结果所示:
#include<iostream>
using namespace std;
void test1(int a , int b)
{
cout <<"a ="<< a <<"\t"<< "b = "<< b <<endl;
}
void test2(int a , int b = 20 )
{
cout <<"a ="<< a <<"\t"<< "b = "<< b <<endl;
}
void test3(int a = 30 , int b = 30 )
{
cout <<"a ="<< a<<"\t" << "b = "<< b <<endl;
}
int main()
{
test1(10,10);
test2(20);
test3();
return 0;
}
需要注意的是,缺省值不可以间隔着给,必须是从右往左依次给,例如下列代码:
void test2(int a , int b = 20 )
{
cout <<"a ="<< a <<"\t"<< "b = "<< b <<endl;
}//此代码可以正常传入实参
void test2(int a =20 , int b )
{
cout <<"a ="<< a <<"\t"<< "b = "<< b <<endl;
}//此代码不可以正常传入实参
extern “C”
加上extern”C“表明在C++工程中,此函数是按照c语言的风格来编译。
extern "C" int add(int a , int b )
{
add(1,2);
return 0;
}//个人不是很理解具体用在何处
引用
引用的定义
类型&别名 = 原名;
例如:
int main ()
{
int a = 2 ;
int&b = a;//此时a和b指向同一内存空间
}//要注意引用的类型要和引用实体的类型一致
引用的时候,需要直接进行引用,不可以只定义(不初始化)之后再引用,这样的话会报错。
int main ()
{
int a = 2;
int &b;//此行代码会报错
}
对用同一个内存空间,可以有多个别名,例如一下程序:
#include<iostream>
using namespace std;
int main()
{
int a = 2;
int& b = a;
int& c = a;
int& d = a;
cout<< "a = " << a <<'\n';
cout<< "b = " << b <<'\n';
cout<< "c = " << c <<'\n';
cout<< "d = " << d <<'\n';
cout <<endl;
return 0;
}
此时的运行结果为:
可以看出此时abcd都等于2,也就说明了,a此时有了bcd三个别名,这四个都指向同一内存空间。
常引用
1.如果定义一个变量为常量,引用时是不可以用非常量的方式进行引用,否则会报错。
int main ()
{
const int a = 10;
int& b = a;//此时a是常量,别名不可以为可读可写,则编译出错
int& b = 10;//10也为常量,编译会出错
}
2.但是如果定义一个变量为非常量,而用常量的方式进行引用的话,这样是可以的。
int main ()
{
int a = 10;
const& b = a;//此时a的权限可读可写,所以可以这样定义。
}
3.引用时,引用类型和引用实体的类型要一致,否则也会报错。
int main ()
{
double a = 2.5;
int& b = a ;//引用类型和实体类型不同,编译出错
}
引用传参
引用传参和指针传参以及传值
传值
#include<iostream>
using namespace std;
void swap(int a , int b);
int main()
{
int a = 1;
int b = 2;
swap(a , b);
cout << "a = " << a << "\t" << "b = " << b << endl;
return 0;
}
void swap(int a , int b)
{
int temp;
temp = a ;
a = b ;
b = temp ;
}
此时运行结果为:
显而易见,当将实参传入到函数中时,只是将值赋给了形参,对形参的值进行了互换,而对实参并没有任何影响,所以只传值,并不能mian函数中的参数互换。
指针传参
#include<iostream>
using namespace std;
void swap(int* a , int* b);
int main()
{
int a = 1;
int b = 2;
int *a1,*b1;
a1 = &a;
b1 = &b;
swap(a1 , b1);
cout << "a = " << a << "\t" << "b = " << b << endl;
return 0;
}
void swap(int* a , int* b)
{
int temp;
temp = *a ;
*a = *b ;
*b = temp ;
cout << "a = " << *a << "\t" << "b = " << *b << endl;
}
指针传参的运行结果:
可以看到,指针传参之后,是将main函数里实参进行了交换,可以得到我们所需要的结果。
因为在main函数中,先设定了两个整型变量,随后又定义了两个指针指向这两个整形变量,此时再进行传参时,所进行的操作是将两个整形变量的地址传入,对这两个地址上的值进行互换,所以不管是形参还是实参,都进行了互换。
引用传参
#include<iostream>
using namespace std;
void swap(int& a , int& b);
int main()
{
int a = 1;
int b = 2;
swap(a , b);
cout << "a = " << a << "\t" << "b = " << b << endl;
return 0;
}
void swap(int& a , int& b)
{
int temp;
temp = a ;
a = b ;
b = temp ;
cout << "a = " << a << "\t" << "b = " << b << endl;
}
此时运行的结果:
可以看出,在进行引用传参时,形参和实参都进行了交换。引用传参的本质其实就是利用指针进行传参数,而且引用传参用起来会比指针传参方便一些。
引用做返回值
(不推荐引用做返回值,除非返回值是全局区变量,否则不推荐使用)
c++程序运行时,会将内存分为以上四个区域,假设有一个函数的返回值不是全局变量、静态变量及常量
#include<iostream>
using namespace std;
int& back();
int main()
{
int& ret = back();
cout << "a = " << ret <<endl;
cout << "a = " << ret <<endl;
return 0;
}
int& back()
{
int a = 2;
return a ;
}
当写入程序后,会出现警告
这个警告说明了,不可以用局部变量引用做返回值。因为这个函数的局部变量在定义后,会存放在栈区,当函数运行完成后,此局部变量会被释放,所以引用做返回值,在局部变量释放后,他并不知道自己是谁的别名,从底层来说,也就是指针并不清楚是指向哪块内存空间。
如果我们是将全局区里的变量可以引用做返回值的话,这样是可以的。例如:
#include<iostream>
using namespace std;
int& back();
int main()
{
int& ret = back();
cout << "a = " << ret <<endl;
//cout << "a = " << ret <<endl;
return 0;
}
int& back()
{
static int a = 2;
return a ;
}
此时在函数里定义了静态变量a,a是存放在全局区,当函数运行完,a并不会直接被释放,所以可以引用做返回值,此程序的运行结果如下:
综上所述,当引用做返回值时,一定要注意返回值是什么变量,是否可以引用。
引用和指针的区别
- 引用相对于指针来说,在传参的效率上,极大的提高了传参效率。
- 引用在定义时必须要进行初始化,而指针可以不用初始化
int main()
{
int a ;
int& b = a ;//必须要进行初始化
int* p;//不用初始化
}
- 没有空引用,但是有空指针(NULL)
- 在sizeof中的含义不同,引用的结果是引用类型的大小,而指针是地址空间所占用的字节数。
- 引用自加即引用的实体增加1,指针自加是指针向后偏移一个数据类型的字节数。
- 引用没有多级引用,但是指针有多级指针。
- 指针需要解指针,但引用编译器可以自己处理。
- 引用比指针使用起来安全一些。
内联函数
以inline修饰的函数叫做内联函数,编译时C++编译器会在调用内联函数的地方展开,没有函数压栈的开销,内联函数提升程序运行的效率。
(还没使用过不怎么理解)
auto关键字
(后续补充)
指针空值nullptr
在c++11中,nullptr表示空指针,而NULL是一个宏定义,其包含在了头文件(stddef.h)中
#define NULL 0
所以在传值调用函数时
void f(int)
{
cout << "f(int)" << endl;
}//NULL会调用此函数
void f(int*)
{
cout << "f(int *)" << endl;
}//nullptr会调用此函数
而在c++17中,NULL的宏定义发生了改变
#define NULL __null//此处的类型为long long
我们只需记住nullptr是空值指针。