0

C++入门基础

 2 years ago
source link: https://blog.csdn.net/xiaomage1213888/article/details/120519576
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

1.C++关键字

C++相比于C语言来说,增添了31个关键字,C++一共有63个关键字。

请添加图片描述


2.C++输入&输出

C++的输入输出相比于C语言来说,相对做了简化,没有了C语言输出时的对类型的约束,不用格式控制符,直接输入输出。

#include <iostream>
using namespace std;
int main()
{
	int a;
	double b;
	char c;
	cin >> a;
	cin >> b >> c;					//输入不需要变量地址
	cout << a << endl;				//换行用endl代替
	cout << b << "  " << c << endl;	 //不用格式控制符,直接输出
	return 0;
}

3.缺省参数

缺省参数就是给函数传递的形式参数给一个默认值,让其在不传参的情况下也能使用。如果有缺省值的情况下,再给函数传参,就用传的参数,缺省值就会失效。

void TestFunc(int a = 0){
	cout << a << endl;
}
int main(){
	TestFunc();     // 没有传参时,使用参数的默认值
	TestFunc(10);   // 传参时,使用指定的实参
}
  • 全缺省参数

就是所有的参数都有默认值

void TestFunc(int a = 10, int b = 20, int c = 30)
{
	cout << "a = " << a << endl;
	cout << "b = " << b << endl;
	cout << "c = " << c << endl;
}
  • 半缺省参数
  • 一部分参数带有缺省值,但必须从有往左给,不能间隔,如果定义了半缺省值的函数,那么调用这个函数时必须传没有缺省值的形参,后面的可以传参,也可以使用缺省值。
  • 缺省参数不能在函数声明和定义中同时出现。
//a.h
void TestFunc(int a = 10);
// a.c
void TestFunc(int a = 20)
{}
//如果声明与定义位置同时出现,恰巧两个位置提供的值不同,那编译器就无法确定到底该用那

  • 缺省值必须是常量或者全局变量。
  • C语言不支持缺省参数

4.函数重载

函数重载就是在同一作用域中。可以声明或定义几个名字相同的函数,这些同名函数的
形参列表(参数个数 或 类型 或 顺序)必须不同,常用来处理实现功能类似数据类型不同的问题

int Add(int left, int right){
	return left + right;
}
double Add(double left, double right){
	return left + right;
}
long Add(long left, long right){
	return left + right;
}

int main(){
	Add(10, 20);			
	Add(10.0, 20.0);
	Add(10L, 20L);					//传不同的形参就可以调用不同的同名函数
	return 0;
}

这几个函数的形参不同,函数名在编译阶段时会被特殊的函数名修饰规则修饰成不同的名字,所以可以区分。

下面的两个函数只有返回值不同,函数名和形参完全相同,而函数名修饰规则里面没有对返回值的修饰,所以这两个函数经过修饰以后形成的名字依然相同,所以这两个函数不形成重载。

short Add(short left, short right){
	return left + right;
}
int Add(short left, short right){
	return left + right;
}
int main(){
    Add(10, 20);
    Add(10, 20);                       //无法确定要调用哪个函数,不形成重载。
	return 0;
}

函数名修饰

不同的编译器和不同的平台拥有不同的函数名修饰规则,原则都是对函数的参数和基本属性进行特殊编码,用来区分函数。C语言的编译器不支持函数名修饰,所以C语言不支持函数重载。


5.extern “C”

有时候在C++工程中可能需要将某些函数按照C的风格来编译,在函数前加extern “C”,意思是告诉编译器,将该函数按照C语言规则来编译。

extern “C” int Add(int left, int right){
	return left + right;
}
extern “C” double Add(double left, double right){
	return left + right;
}
extern “C” long Add(long left, long right){
	return left + right;
}
//如果在刚才形成函数重载的几个函数前面加上extern “C”,那么这三个函数编译时就会按C语言的方式来编译,
//它的函数名就不会被修复,所以这三个函数就不能被重载。
int main(){
	Add(10, 20);			
	Add(10.0, 20.0);
	Add(10L, 20L);					
	return 0;
}

引用就是给变量取别名,但是引用不会开辟新的内存空间,和原来变量拥有共同的地址。

void TestRef() {
	int a = 10;
	int& b = a;

	char ch = 'c';
	char& c = ch;
	//变量的引用
	int ar[10] = { 1,2,3,4,5,6,7,8,9,10 };
	int(&arr)[10] = ar;
	//数组的引用
}

注意:引用类型必须和引用实体是同种类型的.


6.1引用的特性:

  • 引用在定义时必须初始化

  • 一个变量可以有多个引用

  • 引用一旦引用一个实体,再不能引用其他实体

    void TestRef() {
    	int a = 10;
    	int b = 20;
    	// int& ra; 			// 定义时必须初始化,编译时会出错.
    	int& ra = a;
    	int& rra = a;
     	// int& ra = b;          // 一个引用不能引用多个实体,编译时报错.
    }
    

6.2常引用

void TestConstRef() {
	const int a = 10;
	//int& ra = a; 			// 编译时会出错,a为常量
	const int& ra = a;

	// int& b = 10; 		// 编译时会出错,b为常量
	const int& b = 10;		// 引用常量时,引用也必须为常量

	double d = 12.34;
	//int& rd = d; 			// 编译时会出错,类型不同
	const int& rd = d;
}

6.3 引用的使用场景

引用可以用作性形式参数

//void Swap(int a, int b) {
//	int tem = a;
//	int a = b;
//	int b = tem;
//}
// 在c语言中,写一个交换函数,如果传入的参数为普通变量,看似完成了交换,其实函数内部的改变对传入的a,b实参的值并没有影响



//要想让a,b的值发生交换,在传入时可以传入a,b的地址.
void Swap1(int* a, int* b) {
	int tem = *a;
	*a = *b;
	*b = tem;
}
//这样的传参虽然可以实现数据交换,但书写时有些不便
//所以我们可以用传引用来实现数据的交换
void Swap2(int& a, int& b){
	int temp = a;
	a = b;
	b = temp;
}
//这样传递会交换引用的值,也就等于改变了a,b实际空间上的值
int main() {
	int a = 10;
	int b = 20;
	Swap1(&a, &b);		//传地址
	Swap2(a, b);		//传引用
	return 0;
}

引用可以作返回值

//引用作返回值时,返回值一定要是静态变量或者全局变量
int& Count(){
	static int n = 0;
		n++;
	return n;
}

//运行下面的程序时,会出现ret值为7的情况
//因为当函数有返回值时,返回值在返回时并不是直接返回其中的临时变量,函数运行完成时会销毁所有的局部变量,在销毁之前,返回值会被存到一个无名的临时变量之中,然后再拷贝到要赋的值之中.
//若把引用作为返回值,则返回值返回时不会创建无名的临时变量,会返回变量的引用,但是返回之后函数就会被销毁,此时引用的是一个被销毁的空间,这个空间是不受控制的,如果这个空间被修改,则返回的引用值也会跟着改变,所以在返回值为引用时,返回值一定要是全局变量或者静态变量.
int& Add(int a, int b) {
	int c = a + b;
	return c;
}
int main() {
	int& ret = Add(1, 2);
	Add(3, 4);		//此时已经释放的c空间的值 也就是&ret的值被修改
	cout << "Add(1, 2) is :" << ret << endl;  //结果被修改
	return 0;
}

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


//函数返回值为普通变量时,不能作为表达式的左值. 
//返回值为引用时,可以作为左值.
int fun() {
	static int a = 0;
	a++;
	return a;
}

int& fun1() {
	static int a = 0;
	a++;
	return a;
}

int main() {
	//fun() = 100;  //错误
	fun1() = 100;
	return 0;
}

以值作为参数或者返回值类型,在传参和返回期间,函数不会直接传递实参或者将变量本身直接返回,而是传递实参或者返回变量的一份临时的拷贝,因此用值作为参数或者返回值类型,效率是非常低下的,尤其是当参数或者返回值类型非常大时,效率就更低。


6.4 指针和引用的区别

  • 在语法概念上引用就是一个别名,没有独立空间,和其引用实体共用同一块空间。
  • 在底层实现上实际是有空间的,因为引用是按照指针方式来实现的。

引用和指针的汇编代码对比:

请添加图片描述

引用和指针的不同点:

  • 引用在定义时必须初始化,指针没有要求
  • 引用在初始化时引用一个实体后,就不能再引用其他实体,而指针可以在任何时候指向任何一个同类型实体
  • 没有NULL引用,但有NULL指针
  • 在sizeof中含义不同:引用结果为引用类型的大小,但指针始终是地址空间所占字节个数(32位平台下占4个字节)
  • 引用自加即引用的实体增加1,指针自加即指针向后偏移一个类型的大小
  • 有多级指针,但是没有多级引用
  • 访问实体方式不同,指针需要显式解引用,引用编译器自己处理
  • 引用比指针使用起来相对更安全

7.内联函数

概念:

inline修饰的函数叫做内联函数,编译时C++编译器会在调用内联函数的地方展开,没有函数压栈的开销,内联函数提升程序运行的效率

未使用inline修饰时的汇编代码

请添加图片描述

使用inline修饰时的汇编代码

请添加图片描述

上述函数前增加inline关键字将其改成内联函数,在编译期间编译器会用函数体替换函数的调用。

特性

  • inline是一种以空间换时间的做法,省去调用函数额开销。所以代码很长或者有循环/递归的函数不适宜使用作为内联函数。
  • inline对于编译器而言只是一个建议,编译器会自动优化,如果定义为inline的函数体内有循环/递归等等,编译器优化时会忽略掉内联。
  • inline不建议声明和定义分离,分离会导致链接错误。因为inline被展开,就没有函数地址了,链接就会找不到。

8.aotu关键字(C++11)

C++11中,标准委员会赋予了auto全新的含义即:auto不再是一个存储类型指示符,而是作为一个新的类型指示符来指示编译器,auto声明的变量必须由编译器在编译时期推导而得。

int TestAuto() {
	return 10;
}
int main() {
	int a = 10;			
	auto b = a;							
	auto c = 'a';
	auto d = TestAuto();	//新的auto就是用来自动推到变量类型的	
	//auto e;            	// 无法通过编译,使用auto定义变量时必须对其进行初始化
	return 0;
}

注意

​ 使用auto定义变量时必须对其进行初始化,在编译阶段编译器需要根据初始化表达式来推导auto的实际类
型。因此auto并非是一种“类型”的声明,而是一个类型声明时的“占位符”,编译器在编译期会将auto替换为
变量实际的类型。

7.1 auto使用规则

  • auto与指针和引用结合起来使用用auto声明指针类型时,用autoauto*没有任何区别,但用auto声明引用类型时则必须加&
int main() {
	int x = 10;
	auto a = &x;
	auto* b = &x;
    // 此时加不加*都没有影响
	auto& c = x;
    //这时如果不加& 就无法推倒为引用类型
	*a = 20;
	*b = 30;
	c = 40;
	return 0;
}
  • 在同一行定义多个变量当在同一行声明多个变量时,这些变量必须是相同的类型,否则编译器将会报错,因为编译器实际只对第一个类型进行推导,然后用推导出来的类型定义其他变量.
void TestAuto() {
	auto a = 1, b = 2;
	//auto c = 3, d = 4.0; 		// 代码会编译失败,因为c和d的初始化表达式类型不同
}

7.2 auto不能推导的场景

  • auto不能作为函数的参数
// 此处代码编译失败,auto不能作为形参类型,因为编译器无法对a的实际类型进行推导
void TestAuto(auto a) {}
  • auto不能直接用来声明数组
void TestAuto() {
	int a[] = { 1,2,3 };
	//auto b[] = { 4,5,6 };  //不能推倒数组类型
}

9.范围for(C++11)

普通for循环

void TestFor() {
	int array[] = { 1, 2, 3, 4, 5 };
	for (int i = 0; i < sizeof(array) / sizeof(array[0]); ++i)
		array[i] *= 2;
	for (int* p = array; p < array + sizeof(array) / sizeof(array[0]); ++p)
		cout << *p << endl;
}
//这个数组是一个有范围的集合,要遍历,还得自己规定范围,有些多余

9.1 范围for语法

​ 对于一个有范围的集合而言,由程序员来说明循环的范围是多余的,有时候还会容易犯错误。因此C++11中引入了基于范围的for循环。for循环后的括号由冒号“ :”分为两部分:第一部分是范围内用于迭代的变量,第二部分则表示被迭代的范围。

void TestFor() {
	int array[] = { 1, 2, 3, 4, 5 };
	for (auto& e : array)
		e *= 2;
	for (auto e : array)
		cout << e << " ";
}

​ 注意:与普通循环类似,可以用continue来结束本次循环,也可以用break来跳出整个循环。

9.2 范围for的使用条件

  • for循环迭代的范围必须是确定的

    对于数组而言,就是数组中第一个元素和最后一个元素的范围;对于类而言,应该提供begin和end的
    方法,begin和end就是for循环迭代的范围。

    void TestFor(int array[]) {
    	for (auto& e : array)
    		cout << e << endl;
    }
    //此时数组的范围不确定,所以无法使用范围for循环
    

10.指针空值nullptr(C++11)

C++98中的指针空值

指针的初始化

void TestPtr() {
	int* p1 = NULL;
	int* p2 = 0;
}

NULL实际是一个宏,在传统的C头文件(stddef.h)中,可以看到如下代码:

#ifndef NULL
#ifdef __cplusplus
#define NULL 0
#else
#define NULL ((void *)0)
#endif
#endif

可以看到,NULL可能被定义为字面常量0,或者被定义为无类型指针(void)的常量。不论采取何种定义,在使用空值的指针时,都不可避免的会遇到一些麻烦,比如:*

void fun(int) {
	cout << "fun(int)" << endl;
}
void fun(int*) {
	cout << "fun(int*)" << endl;
}
int main() {
	fun(0);
	fun(NULL);
	fun((int*)NULL);
    //由于NULL是0,所以在函数重载时本来传的是指针,却被认为是整形,就会调错函数
    //如果要将其按照指针方式来使用,必须对其进行强转
	return 0;
}

请添加图片描述

注意:

  • 在使用nullptr表示指针空值时,不需要包含头文件,因为nullptr是C++11作为新关键字引入的。
  • 在C++11中,sizeof(nullptr) 与 sizeof((void)0)所占的字节数相同。
  • 为了提高代码的健壮性,在后续表示指针空值时建议最好使用nullptr

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK