当前位置: 首页 > article >正文

【C++】入门C++

1.C++的第一个程序

之前写的C语言文件都是后缀为.c的文件,进入C++后就要把后缀改为.c++了,vs编译器看到是.cpp就会调⽤C++编译器编译。C++兼容C语言的绝大多数语法,所以C语言的 hallo word 依旧可以在C++下使用。

//test.cpp
//c语言的hallo world
#include<stdio.h>
int main() {

	printf("%s", "hallo world\n");

	return 0;
}

当然C++也有自己的输入输出,C++的hallo word长这样:

//test.cpp
//c++的hallo world
#include<iostream>//相当于c语言的.h文件
using namespace std;
int main() {

	cout << "hello world\n";

	return 0;
}

2.命名空间

2.1命名空间的价值

在C/C++ 中,标准库中的函数命名与程序员写的函数命名、程序员和程序员之间的(函数,变量,宏等等)这些可能会存在命名冲突,使用命名空间(namespace)可以有效避免命名冲突,使得代码更加模块化和易于管理。

c语⾔项⽬类似下⾯程序这样的命名冲突是普遍存在的问题,C++引⼊namespace就是为了更好的解决这样的问题
//c语言的命名冲突

#include<stdio.h>
#include<stdlib.h> //rand()函数在这个库中,编译链接后展开

int rand = 10;

int main() {
	//C2365 "rand":重定义,以前的定义是"函数"

	printf("%d", rand);

	return 0;
}

2.2命名空间的定义 

  • 命名空间的定义需要用到namespac关键字,后面接命名空间的名字,然后在加一对{}即可。{}中即为命名空间的成员。命名空间可以定义变量/函数/类型等。
  • 命名空间的本质是定义了一个新的域,这个域跟全局域互相独立,不同的域可以定义同名变量。这样在命名空间中定义一个rand变量就不会和全局域中的rand函数冲突了。
  • C++中有函数局部域,全局域,命名空间域类域,类域。域影响的是编译器在编译时查找一个变量/函数/类型出处(定义或声明)的逻辑,所以有了域的隔离,名字冲突问题自然就解决了。局部域和全局域除了会影响查找逻辑,还会直接影变量的声明周期,而命名空间域和类域不会直接影响变量的生命周期。
  • 命名空间只能定义在全局,它也可以嵌套定义。
  • 项目工程中的多个文件,会将同名的命名空间当成一个,不会有冲突
  • C++的标准库都放在一个std(standard)的命名空间域中

普通的命名空间域定义

#include<iostream>
#include<stdlib.h>

//普通的命名空间定义 jiuwu是这个命名空间的名字

namespace jiuwu
{
	int a = 10;

	int ADD(int a, int b)
	{
		return a + b;
	}

	struct Node
	{
		struct Node* next;
		int val;
	};
}
int main() {
	
	//这的rand是全局域中的rand函数
	printf("%p\n", rand);

	//这是jiuwu命名空间域中的rand变量
	printf("%d\n",jiuwu::a);

	return 0;
}

嵌套定义命名空间域

#include<iostream>
#include<stdlib.h>

//嵌套命名空间定义

namespace XiangMu_1
{
	namespace jiuwu
	{
		int a = 10;

		int ADD(int a, int b)
		{
			return a + b;
		}

		struct Node
		{
			struct Node* next;
			int val;
		};
	}
	namespace sidangkang
	{
		int a = 20;

		int ADD(float a, float b)
		{
			return a + b;
		}

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

}
int main() {
	
	//这的a是命名空间XiangMu_1中的嵌套jiuwu中的a
	printf("%d\n", XiangMu_1::jiuwu::a);

	//这的a是命名空间XiangMu_1中的嵌套sidangkang中的a
	printf("%d\n", XiangMu_1::sidangkang::a);

	return 0;
}

2.3命名空间的使用

编译器在查找一个变量的声明/定义时,默认只会在局部域和全局域中查找,不会到命名空间域中查找,所以以下程序会报错
# include <stdio.h>
namespace jiuwu
{
int a = 0 ;
int b = 1 ;
}
int main ()
{
// 编译报错: error C2065: “a”: 未声明的标识符
printf ( "%d\n" , a);
return 0
}

我们要使用命名空间中定义的变量/函数,有三种方式:  

  • 指定命名空间访问,项目中推荐这种方式。
  • using将命名空间中某个成员展开,项目中经常访问的不存在冲突的成员推荐这种方式。
  • 展开命名空间中全部成员,项目不推荐,冲突风险很大,日常小练习程序为了方便推荐使用。
namespace jiuwu
{
int a = 0;
int b = 1;
}


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

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

//将整个命名空间域展开
using namespce jiuwu;
int main()
{
printf("%d\n", a);
printf("%d\n", b);
return 0;
}

3.C++的输入&输出

  • <iostream> 是C++的标准输入/输出库,定于了标准输入输出对象
  • std::cin 是istream的对象,它主要面向窄字符的标准输入流
  • std::cout是ostream的的对象,它主要面向窄字符的标准输出流
  • std::endl是一个函数,流插入输出时,相当于一个换行字符加刷新缓冲区
  • <<是流插入运算符,>>是流提取运算符 (c语言还使用它们做为移位运算符)
  • C++的输入输出非常方便,它不用向printf函数那样指定类型,它可以自己识别类型
  • cout/cin/endl都属于C++的标准库,C++的标准库都放在一个叫std(standard)的命名空间中,所以要同命名空间的使用方法去使用它们
  • <iostream>间接包含了<stdio.h>,使用可以直接使用printf和scanf,vs系列的编译器是这样的,其他编译器可能会报错
#include<iostream>
int main() {
	int a;
	float b;
	char c;

	//cin = scanf 
	//>>从标准输入流中提取数据
	std::cin >> a >> b >> c; //可自动识别数据类型

	//cout = printf
	//<<将数据输入到标准输出流中
	std::cout << a << " "<< b <<" " << c <<std::endl;

	
	return 0;
}

4.缺省参数

  • 缺省参数就是在定义或声明函数时为函数的形参指定一个默认值。在调用参数时,如果没有对应的实参那么就会使用设定好的缺省值,否则使用实参。缺省参数右分为全缺省参数与半缺省参数。
  • 全缺省参数就是将函数所有的新参都设计为缺省参数,半缺省参数就是将函数部分参数设置为缺省参数。C++规定半缺省参数只能从右往左连续缺省
  • 带缺省参数的函数的调用,C++规定只能从左往右连续给实参,不能跳跃给实参。
  • 函数声明定义分离时,规定缺省参数必须在声明中给。
#include<iostream>
using namespace std;
//全缺省
void func(int a = 10, int b = 10, int c = 10)
{
	cout << a << endl << b << endl << c;
}

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

//错误半缺省
void func2(int a = 10, int b, int c)//应从右往左连续设置缺省值
{
	cout << a << endl << b << endl << c;
}



int main() {

	func();//在没有实参时使用设计好的缺省值
	func(1);//传参时,使用指定实参

	func1();//不能再没有设置缺省参数的地方,选着不传实参
	func1(1);//这里至少传一个实参
	func1(1, 2);

	return 0;
}

5.函数重载

C++中规定可以在同一作用域中出现同名函数,但是要求它们的形参不同

//函数重载
// 1、参数类型不同
int Add(int left, int right)
{
	cout << "int Add(int left, int right)" << endl;
	return left + right;
}

double Add(double left, double right)
{
	cout << "double Add(int left, int right)" << endl;
	return double + double;
}

// 2、参数个数不同
void f()
{
cout << "f()" << endl;
}
void f(int a)
{
cout << "f(int a)" << endl;
}

// 3、参数顺序不同
void f(char b,int a)
{
cout << "f(char b,int a)" << endl;
}
void f(int a,char b)
{
cout << "f(int a,char b)" << endl;
}

//注意!不能仅通过返回值,与返回类型进行函数重载
int Add(int left, int right)
{
	cout << "int Add(int left, int right)" << endl;
	return left + right;
}

double Add(int left, int right)//返回类型不能作为重载的条件
{
	cout << "int Add(int left, int right)" << endl;
	return left + right;
}

6.引用

6.1什么是引用

 引用不是新定义一个变量,而是给现有的变量取一个别名,这个别名并不会新开辟一块内存空间,而是会和被取别名的变量一起使用一块空间。

类型 & 引用别名 = 引用对象 

#include<iostream>
using namespace std;
int main() {

	int a = 10;

	//给a这个变量取别名
	int& b = a;
	int& c = a;

	//也可以给别名取别名,但终究还是同一个变量】
	int& d = b;

	//这里我们可以发现它们的内容和地址都是完全一样的
	cout << a << "    " << &a << endl;
	cout << b << "    " << &b << endl;
	cout << c << "    " << &c << endl;
	cout << d << "    " << &d << endl;


	return 0;
}

6.2引用的特性

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

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

3.引用一旦被初始化,就不能再改变其指向的对象

4.由于:引用一旦被初始化,就不能再改变其指向的对象,所以在使用const修饰引用时只能修饰其“内容”

#include<iostream>
using namespace std;
int main() {

	//引用在定义时必须初始化,否则报错
	//int& a;

	//一个变量可有多个引用
	int b = 10;
	int& c = b;
	int& d = b;

	//这⾥不是让f引⽤b,因为C++引⽤不能改变指向,这里是给f赋值即 f = 10;e = 10
	int e = 20;
	int& f = e;
	cout << f<<endl;
	f = b;
	cout << f<<endl;

    //const修饰引用
    const int& ra = a;//正确

    int& const ra = a;//错误 原因:引用本质上是一个已经绑定了某个对象的别名,其“指向”在初始化后是不可变的。因此,为引用添加 const 修饰其“指向”是没有意义的,也是不被编译器允许的。


	return 0;
}

6.3const引用

当引用一个const对象,必须使用const引用const引用也可以引用普通对象,因为对象的访问权限可以在引用时缩小,但是不能在引用时放大。

当我们引用编译器创建的临时对象时,也需要用const引用。因为临时对象体通常是只读的,对它进行引用而不使用const引用会造成权限的放大

临时对象的产生

1.表达式的结果

某个表达式的结果会被编译器会创建一个临时对象来保存这个结果

//10 + 20 的结果会被存储到临时对象中,然后在赋值给a
int a = 10 + 20;

2.类型转换

当执行显式的类型转换或隐式的类型转换,且转换结果需要被存储时,也可能产生临时对象

//10.2类型转换的结果会被存储在临时对象中,然后在被赋值被a
int b = 10.2;

3.常量引用的绑定

// 编译器会创建一个值为10的临时int对象
const int& a = 10; 

临时对象的生命周期:临时对象的生命周期是自动管理的,并且通常与引用它的作用域相同。一旦引用超出了它的作用域,临时对象就会被销毁。

6.4.指针和引用的区别

  • 在语法层面,引用的创建不需要开空间属于引用对象的别名,而指针的创建需要空间
  • 引用在定义时必须初始化,而指针不用(指针不初始化容易造成野指针问题)
  • 引用在指向一个对象后不能在更改,而指针可以随时更换指向对象
  • 引用可直接访问指向对象,而指针需要解引用
  • sizeof中的含义不同,引用结果为引用对象类型大小,但指针永远是固定的字节(在32位系统上,指针的大小通常是4字节,在64位系统上,指针的大小通常是8字节)

7.内联函数        

内联函数只需要在函数前面加上inline关键字,编译器会在有内联函数的地方选择性的展开,这样就不用新开栈帧了,对效率提升有所帮助

内联函数的展开对于编译器来说只是一个建议,对于程序员不合理的展开,编译器会选择视而不见

内联函数的声明和定义不建议分离在不同的源文件中,这样会导致链接错误。因为inline展开就没有地址,编译器就找不到定义的内联函数

在vs编译器debug状态进行反汇编码的观察,我们发现内联函数并未展开,这是为了方便程序员调试,debug状态下展开内联函数需要设置这两个地方

 

#include<iostream>
using namespace std;

// 正确的宏实现
#define ADD(a, b) ((a) + (b))
// 为什么不能加分号?
// 为什么要加外⾯的括号?
// 为什么要加⾥⾯的括号?

int main() {

	cout << ADD(2, 3) << endl;//如果加了分号就会出现语法错误

	cout << ADD(2, 3) * 4 << endl;//如果不加外面括号->(a)+ (b) * 4,b会先与4乘法运算,跟我们实际需求不符

	int a = 1, b = 2;
	cout << ADD(a | b, b & a) << endl;//如果不加里面的括号->(a | b + b  &a)b 会与 b先进行加法运算

	return 0;
}

8.nullptr

在C++和C中,NULL实际上是一个宏,C++中NULL被替换成 0,C中NULL被替换成(void*)0。但是无论这两种定义,在C++中使用它们的时候都会出一些问题,例如:

void func(int);  
void func(int*);

假如你想调用 void func(int*);你可能会这样做:

func(NULL);

但是由于NULL被替换成了字面量0,所以你实际上调用的是int版本的func函数

你还可能会这样做:

funk((void*)NULL);

这样写在C++中也是错误的,因为在C++中void*指针不能够隐形的转化为其他类型的指针,在C语言中这样写是可以的,因为C语言中的void*指针可以给给任意类型的指针

所以在C++11中引入了nullptr关键字。nullptr实际上是一种特殊的字面量,它能够隐形的转化成任意类型的指针,而不能过转化成整数类型

#include<iostream>
using namespace std;
void func(int a)
{
	cout << "func(int a)" << endl;
}
void func(int* a)
{
	cout << "func(int* a)" << endl;
}
int main() {

	func(NULL);
	//func((void*)NULL);

	func(nullptr);

	return 0;
}

http://www.kler.cn/a/371450.html

相关文章:

  • 在qemu-system上跑arm-Debian
  • 如何使用python完成最简单的数据预测?
  • Android的SQLiteOpenHelper类 笔记241027
  • 软件系统安全保证措施,质量保证措施方案(Word原件套用)
  • 【AI模特试衣整合包及教程】CatVTON引领虚拟试衣新时代
  • c++习题36-奇数单增序列
  • Leetcode 第 420 场周赛题解
  • 数学建模清风——论文写作方法教程笔记
  • 无人机之多源信息融合算法篇
  • 【ms-swift 大模型微调实战】
  • 自动化机器学习(AutoML)详解
  • 【Spring】IocDI
  • 端到端自动驾驶模型SparseDrive论文阅读笔记
  • HarmonyOS:@Watch装饰器:状态变量更改通知
  • webAssembly初探
  • Python BeautifulSoup 实战案例:抓取网页并提取数据
  • 【C++】智能指针:解决内存泄漏、悬空指针等问题
  • 【Unity基础】初识UI Toolkit - 编辑器UI
  • 简记 Vue3(一)—— setup、ref、reactive、toRefs、toRef
  • PyTorch模块介绍
  • 【IC每日一题】
  • 【架构-36】常见的各类锁的特点
  • C++学习:类和对象(二)
  • [ 问题解决篇 ] 解决远程桌面安全登录框的问题
  • 3个模型的交互式多模型IMM,基于EKF的目标跟踪实例(附MATLAB代码)
  • java web调试时清理当前网址的缓存