一、C++基础

目录

什么是C++

C++的发展史

命名空间

命名空间的定义

1.正常的命名空间定义

 2.命名空间可以嵌套

3.多⽂件中可以定义同名namespace,他们会默认合并到⼀起,就像同⼀个namespace⼀样

命名空间的使用

1.指定命名空间访问

2.using将命名空间中的某个成员展开

3. 展开命名空间中的全部成员

 C++的输入与输出

缺省参数

全缺省

半缺省

函数重载

引用

引用的特性

 const引用

 指针和引用的关系

内联函数(inline)

内联函数的使用

指针空值(nullptr)


什么是C++

C++(c plus plus)是一种计算机高级程序设计语言,由C语言扩展升级而产生,最早于1979年由本贾尼·斯特劳斯特卢普在AT&T贝尔工作室研发。C++既可以进行C语言的过程化程序设计,又可以进行以抽象数据类型为特点的基于对象的程序设计,还可以进行以继承和多态为特点的面向对象的程序设计。C++擅长面向对象程序设计的同时,还可以进行基于过程的程序设计。

C++的发展史

20世纪70年代中期,本贾尼·斯特劳斯特卢普在剑桥大学计算机中心工作。斯特劳斯特卢普希望开发一个既要编程简单、正确可靠,又要运行高效、可移植的计算机程序设计语言。而以C语言为背景,以Simula思想为基础的语言,正好符合斯特劳斯特卢普的初衷和设想。1979年,本贾尼·斯特劳斯特卢普到了AT&T贝尔实验室,开始从事将C改良为带类的C(C with classes)的工作。、1983年,该语言被正式命名为C++。让我们来看看C++的历史版本。

命名空间

在C/C++中,变量、函数和后⾯要学到的类都是⼤量存在的,这些变量、函数和类的名称将都存在于全 局作⽤域中,可能会导致很多冲突。使⽤命名空间的⽬的是对标识符的名称进⾏本地化,以避免命名 冲突或名字污染,namespace关键字的出现就是针对这种问题的。

特别注意C++标准库都放在⼀个叫std(standard)的命名空间中。

命名空间的定义

定义命名空间,需要使⽤到namespace关键字,后⾯跟命名空间的名字,然后接⼀对{}即可,{}中 即为命名空间的成员。命名空间中可以定义变量/函数/类型等。

1.正常的命名空间定义

//1.正常的命名空间
namespace yxt
{
	int rand = 10;

	int ADD(int left, int right)
	{
		return left + right;
	}

	struct Node
	{
		int val;
		struct Node* next;
	};
}

 2.命名空间可以嵌套

//2.命名空间可以嵌套
namespace yxt
{
	namespace a1
	{
		int rand = 1;

		int ADD(int left, int right)
		{
			return left + right;
		}
	}

	namespace a2
	{
		int rand = 2;

		int ADD(int left, int right)
		{
			return (left + right)*10;
		}
	}
}

3.多⽂件中可以定义同名namespace,他们会默认合并到⼀起,就像同⼀个namespace⼀样

//Stack.h
#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<stdbool.h>
#include<assert.h>
namespace yxt
{
	typedef int STDataType;
	typedef struct Stack
	{
		STDataType* a;
		int top;
		int capacity;
	}ST;
	void STInit(ST* ps, int n);
}

//Stack.cpp
#include "Stack.h"
namespace yxt
{
	void STInit(ST* ps, int n)
	{
		assert(ps);
		ps->a = (STDataType*)malloc(n * sizeof(STDataType));
		ps->top = 0;
		ps->capacity = n;
	}
}

命名空间的使用

程序编译查找⼀个变量的声明/定义时,默认只会在局部或者全局查找,不会到命名空间⾥⾯去查找。所以我们要使⽤命名空间中定义的变量/函数,有三种⽅式:

1.指定命名空间访问

2.using将命名空间中的某个成员展开

3.展开命名空间中的全部成员

符号"::"在C++中叫做域作用限定符,我们可以通过 “命名空间名称::命名空间成员”来访问到命名空间中相对应的成员。 

1.指定命名空间访问

//指定命名空间访问
namespace yxt
{
	int a = 1;
	int b = 2;
}
int main()
{
	printf("%d\n", yxt::a);
	return 0;
}

2.using将命名空间中的某个成员展开

//using将命名空间中的某个成员展开
namespace yxt
{
	int a = 1;
	int b = 2;
}
using yxt::b;
int main()
{
	printf("%d\n", yxt::a);
	printf("%d\n", b);
	return 0;
}

3. 展开命名空间中的全部成员

//展开命名空间中的全部成员
namespace yxt
{
	int a = 1;
	int b = 2;
}
using namespace yxt;
int main()
{
	printf("%d\n", a);
	printf("%d\n", b);
	return 0;
}

 C++的输入与输出

在C语言中由标准输入输出函数scanf和printf,而在C++中有与之对应的标准输入流cin和标准输出流cout。使用时需包含头文件iostream以及通过命名空间std来使用他们。

相比C语言,C++的输入输出更加方便,因为其输入输出不需要控制格式,可以自动的识别变量的类型。 

#include<iostream>
using namespace std;

int main()
{
	int a = 0;
	double b = 0.1;
	char c = 'x';
	
	cout << a << " " << b << " " << c << endl;
	std::cout << a << " " << b << " " << c << std::endl;


	// 可以⾃动识别变量的类型
	cin >> a >> b >> c;

	cout << a << " " << b << " " << c << endl;
	return 0;
}

此处的endl是一个函数, 流插⼊输出时,相当于插⼊⼀个换⾏字符加刷新缓冲区,作用相当于换行。

缺省参数

缺省参数是声明或定义函数时为函数的参数指定⼀个缺省值。在调⽤该函数时,如果没有指定实参 则采⽤该形参的缺省值,否则使⽤指定的实参,缺省参数分为全缺省和半缺省参数。(有时也叫默认参数)

void func(int a = 0)
{
	cout << a << endl;
}

int main()
{
	func();		//没有传参时,默认使用参数的默认值
	func(10);	//传参时,使用指定的实参
	return 0;
}

全缺省

全缺省就是全部形参给缺省值 

//全缺省
void func1(int a = 10, int b = 20, int c = 30)
{
	cout << "a = " << a << endl;
	cout << "b = " << b << endl;
	cout << "c = " << c << endl << endl;
}

int main()
{
	func1();
	func1(1);
	func1(1, 2);
	func1(1, 2, 3);
	return 0;
}

半缺省

半缺省就是部分形参给缺省值 

//半缺省
void func2(int a, int b = 20, int c = 30)
{
	cout << "a = " << a << endl;
	cout << "b = " << b << endl;
	cout << "c = " << c << endl << endl;
}

int main()
{
	func2(1);
	func2(1, 2);
	func2(1, 2, 3);
	return 0;
}

缺省参数有以下注意的点:

1.C++规定半缺省参数必须从右往左依次连续缺省,不能间隔跳跃给缺省值。 

2.带缺省参数的函数调⽤,C++规定必须从左到右依次给实参,不能跳跃给实参。

3.函数声明和定义分离时,缺省参数不能在函数声明和定义中同时出现,规定必须函数声明给缺省值。

//Stack.h
#pragma once
#include<stdio.h>
#include<stdlib.h>
#include<stdbool.h>
#include<assert.h>

typedef int STDataType;
typedef struct Stack
{
	STDataType* a;
	int top;
	int capacity;
}ST;
void STInit(ST* ps, int n = 4);

//Stack.cpp
#include "Stack.h"

void STInit(ST* ps, int n)
{
	assert(ps);
	ps->a = (STDataType*)malloc(n * sizeof(STDataType));
	ps->top = 0;
	ps->capacity = n;
}

//test.cpp
#include"Stack.h"

int main()
{
	ST s1;
	STInit(&s1);

	//如果已经确定要插入500个数据,初始化时,直接开好空间,避免扩容。
	ST s2;
	STInit(&s2, 500);
	return 0;
}

函数重载

C++⽀持在同⼀作用域中出现同名函数,但是要求这些同名函数的形参不同,可以是参数个数不同或者类型不同。这样C++函数调⽤就表现出了多态⾏为,使⽤更灵活。C语⾔是不⽀持同⼀作⽤域中出现同名函数的。

若只有返回值不同,其余均相同,则不构成函数重载。 

//1.参数类型不同

int ADD(int x, int y)
{
	return x + y;
}

double ADD(double x, double y)
{
	return x + y;
}

//2.参数个数不同

void f()
{
	cout << "f()" << endl;
}

void f(int a)
{
	cout << "f(int a)" << endl;
}

//3.参数类型顺序不同

void f(int a, char b)
{
	cout << "f(int a,char b)" << endl;
}

void f(char b, int a)
{
	cout << "f(char b,int a)" << endl;
}

引用

 引⽤不是新定义⼀个变量,⽽是给已存在变量取了⼀个别名,编译器不会为引用变量开辟内存空间, 它和它引⽤的变量共用同⼀块内存空间。

类型& 引⽤别名 = 引⽤对象;

int main()
{
	int a = 1;
	//引用:b和c是a的别名
	int& b = a;
	int& c = a;
	//也可以给别名起别名,相当于d还是a的别名
	int& d = b;
	//改变d相当于改变a
	++d;
	cout << a << endl;

	//取地址后我们发现地址均相同
	cout << &a << endl;
	cout << &b << endl;
	cout << &c << endl;
	cout << &d << endl;
	return 0;
}

引用的特性

1.引用在定义时必须初始化。

2.一个变量可以有多个引用。

3.引用一旦引用一个实体,再不能引用其他实体。 (引用不能改变指向)

 const引用

可以引⽤⼀个const对象,但是必须⽤const引⽤。const引⽤也可以引⽤普通对象,因为对象的访 问权限在引⽤过程中可以缩⼩,但是不能放⼤。

int main()
{
	const int a = 10;

	int& ra = a;
	//编译会报错,因为a本身为常量只可读不可写,这样引用使得对a访问权限放大
	//正确的是
	const int& ra = a;

	//这里的引用是对b访问权限的缩小
	int b = 20;
	const int& rb = b;
	return 0;
}

下面我们来看这么一段代码 

int main()
{
	double b = 1.23;
	int& rb = b;
	return 0;
}

第一眼可能我们认为是对的,但结果是代码会报错。想搞明白其中的原因,我么要知道隐式类型转换。从double变为int的过程中会发生隐式类型转换,在这个过程中编译器会产生一个空间来临时存储中间值(也叫临时对象),而C++中规定临时对象具有常性,所以这里触发了权限放大,必须用常引用来解决。 

int main()
{
	double b = 1.23;
	const int& rb = b;
	return 0;
}

 指针和引用的关系

1.语法概念上引用是一个变量的别名,不额外开辟空间,指针存储一个变量的地址,额外开辟空间。

2.引用在定义时必须初始化,指针则不是必须初始化。

3.引用在初始化引用一个对象后,就不能引用其他对象;指针可以不断改变指向对象。

4.引用可以直接访问引用对象;指针需要解引用才能访问指向对象。

5.指针很容易出现空指针和野指针的现象;引用很少出现,相对更安全。

6.sizeof中含义不同,引用结果为引用类型的大小;指针是地址空间所占字节数(32位平台下是4个字节,64位平台下是8个字节)。

内联函数(inline)

⽤inline修饰的函数叫做内联函数,编译时C++编译器会在调用的地方展开内联函数,这样调⽤内联函数就需要建⽴栈帧了,就可以提⾼效率。 

看到展开函数,我们可能会下意识的想到C语言中的宏函数。

C语⾔实现宏函数会在预处理时替换展开,但是宏函数实现很复杂很容易出错的,且不⽅便调 试,C++设计了inline⽬的就是替代C的宏函数。 

inline int ADD(int x, int y)
{
	int ret = x + y;
	return ret;
}

int main()
{
	int ret = ADD(1, 2);
	return 0;
}

内联函数的使用

1.inline是一种以空间换时间的做法,因此对编译器来说只是一个建议,不同编译器关于inline什么情况展开各不相同。 inline适用于频繁调用的短小函数。对于递归函数或者代码相对长一点的函数来说,即使加上inline也会被编译器忽视。

2.inline不建议声明和定义分离到两个文件,分离会导致链接错误。因为在inline被展开时,就没有了函数地址,链接时会报错。

指针空值(nullptr)

在C语言中,我们常常用到NULL,NULL其实是一个宏,在传统的C头⽂件(stddef.h)中,可以看到如下代码: 

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

可以看到NULL可能被定义为常量0,或者定义为(void*)类型的常量0。不管使用哪种定义,都会不可避免的遇到麻烦,比如: 

void fun(int x)
{
	cout << "fun(int)" << endl;
}

void fun(int* ptr)
{
	cout << "fun(int*)" << endl;
}

int main()
{
	fun(0);				//结果为fun(int)
	fun(NULL);			//结果为fun(int)
	fun((int*)NULL);	//结果为fun(int*)
	return 0;
}

在fun(NULL)中,本意是调用指针版本的fun(int* ptr),但由于NULL被定义为0,最终调用的是fun(int x)。 

为了解决这个问题,C++11中引⼊nullptr,nullptr是⼀个特殊的关键字,nullptr是⼀种特殊类型的字⾯量,它可以转换成任意其他类型的指针类型。使⽤nullptr定义空指针可以避免类型转换的问题,因为nullptr只能被隐式地转换为指针类型,⽽不能被转换为整数类型 。

  • 16
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值