前往小程序,Get更优阅读体验!
立即前往
发布
社区首页 >专栏 >编码之旅:C++的基础韵律

编码之旅:C++的基础韵律

作者头像
风中的云彩
发布2024-11-07 21:54:02
发布2024-11-07 21:54:02
750
举报
文章被收录于专栏:C/C++的自学之路

少年不惧岁月长,彼方尚有荣光在。 

前言

这是我自己学习C++的第一篇博客总结。后期我会继续把C++学习笔记开源至博客上。

C++的兼容性

1. C++兼容绝大多数C语言的语法,因此只需要把 .c 后缀文件改为 .cpp 即可。 2. VS编译器看到是.cpp就会调用C++编译器编译。

代码语言:javascript
复制
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{
	printf("hello world\n");
	return 0;
}
//hello world

//实际上应该这样写
#include <iostream>
using namespace std;//将标准库展开后,调用标准库的函数时,就可以不需要作用域限定符。
int main()
{
	cout << "hello world" << endl;
	return 0;
}
//hello world

命名空间

namespace关键字

1. 在C/C++中,变量、函数和后面要学到的类都是大量存在的,这些变量、函数和类的名称都存在于全局作用域中,可能会导致很多冲突。 2. 使用命名空间的目的是对标识符的名称进行本地化,以避免命名冲突或名字污染。 3. 定义命名空间时,需要使用到namespace关键字,后面跟命名空间的名字,然后接⼀对 { } 即可,{ } 中即为命名空间的成员。命名空间中可以定义变量、函数、类型等。 4. namespace本质是定义⼀个域,创建一个命名空间,这个域跟其他的域相互独立。 5. 不同的域可以定义同名变量,但同一个域不能定义同名变量。 6. 命名空间域和类域只能在全局域中定义,原本就是为了在全局域中进行隔离。所以里面的变量、函数的生命周期都是全局的。 7. 命名空间域里面可以继续嵌套命名空间域,使用里面内容时需要多用几个作用域限定符。 8. 多个文件中可以定义同名namespace,它们会默认合并到⼀起,就像同⼀个namespace⼀样。 9. C++标准库都放在⼀个叫std(standard)的命名空间中。 10. 域分为局部域、全局域、名命空间域和类域。

代码语言:javascript
复制
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
namespace hsy
{
	int rand = 10;//命名空间域相当于在全局域中划出一片区域,形成独立的命名空间域
}
int main()
{
	printf("%p\n", rand);//00007FFD2F024D50
	printf("%d\n", hsy::rand);//10
    return 0;
}
代码语言:javascript
复制
x//局部变量的x
::x//全局变量的x
hsy::x//命名空间hsy的x
:://域作用限定符
代码语言:javascript
复制
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
namespace hsy
{
	namespace yhy
	{
		int rand = 2024;
	}
	namespace sbd
	{
		int rand = 2023;
	}
}
//命名空间域里面可以继续嵌套命名空间域。
int main()
{
	printf("%p\n", rand);//00007FFD2F024D50
	printf("%d\n", hsy::yhy::rand);//2024
	printf("%d\n", hsy::sbd::rand);//2023
    return 0;
}

命名空间的使用

1. 编译查找⼀个变量的声明、定义时,默认只会在局部或者全局查找,不会到命名空间里面去查找。 2. 所以我们要使用命名空间中定义的变量、函数,有三种方式:

  1. 指定命名空间访问。
  2. using将命名空间中某个成员展开。
  3. using展开命名空间中全部成员。
代码语言:javascript
复制
#include<stdio.h>
namespace hsy
{
	int a = 0;
	int b = 1;
}

int main()
{
	printf("%d\n", a);// 编译报错:error C2065: “a”: 未声明的标识符
	return 0;
}

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

using hsy::b;// using将命名空间中某个成员展开,相当于把命名空间域hsy里面的东西暴露在全局域中
int main()
{
	printf("%d\n", hsy::b);
	printf("%d\n", b);//效果相同
	return 0;
}

using namespce hsy;// 展开命名空间中全部成员
int main()
{
    printf("%d\n", a);
    printf("%d\n", b);
    return 0;
}

C++的输入输出

1. <iostream>头文件,是Input Output Stream 的缩写,包括了标准的输入、输出流库,定义了标准的输入、输出对象。 2. <<是''流插入''运算符,>>是''流提取''运算符。 3. std::cin 是 istream 类的对象,标准输入流。 4. std::cout 是 ostream 类的对象,标准输出流。 5. std::endl 是⼀个函数,流插入输出时,相当于插入⼀个换行字符加刷新缓冲区。 6. 使用C++输入输出更方便,不需要像printf/scanf输入输出时那样,需要手动指定格式,C++的输入输出可以自动识别变量类型。 7. cout/cin/endl等都属于C++标准库,C++标准库都放在⼀个叫std(standard)的命名空间域中,所以要通过命名空间的使用方式去用他们。 8. ⼀般日常练习中我们可以 using namespace std;。(使用标准库函数时候更方便)

代码语言:javascript
复制
#include <iostream>
using namespace std;
int main()
{
	int a = 0;
	cout << a << " " << 'b' << endl;// 可以⾃动识别变量的类型
	std::cout << a << " " << 'b' << std::endl;// 可以⾃动识别变量的类型
	return 0;
}
//0 b
//0 b

缺省参数

1. 缺省参数是声明或定义函数时为函数的参数指定⼀个缺省值。 2. 在调用该函数时,如果没有指定实参,则采用该形参的缺省值,否则使用指定的实参,缺省参数分为全缺省和半缺省参数。 3.  全缺省就是全部形参给缺省值,半缺省就是部分形参给缺省值。 4.  C++规定半缺省参数必须从右往左 依次连续缺省,不能间隔跳跃给缺省值。 5.  函数声明和定义分离时,缺省参数不能在函数声明和定义中同时出现,规定必须函数声明给缺省 值。

代码语言:javascript
复制
#include <iostream>
using namespace std;
void Add(int a = 10, int b = 5)
{
	cout<<(a + b)<<endl;
}
void Func1(int a = 10, int b = 20, int c = 30)
{
	cout << a << endl;
	cout << b << endl;
	cout << c << endl;
}
int main()
{
	Add(1, 2);//有传实参时候,使用实参值
	Add();//没有传实参时候,使用参数默认值
	Func1();
	Func1(1);
	Func1(1,2);
	Func1(1,2,3);
	Func1(, 2, );//格式错误,不能跳跃
}
代码语言:javascript
复制
// Stack.h
#include <iostream>
#include <assert.h>
using namespace std;
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 && n > 0);
	ps->a = (STDataType*)malloc(n * sizeof(STDataType));
	ps->top = 0;
	ps->capacity = n;
}

// test.cpp
#include"Stack.h"
int main()
{
	ST s1;
	STInit(&s1);
	// 确定知道要插⼊1000个数据,初始化时⼀把开好,避免扩容
	ST s2;
	STInit(&s2, 1000);
	return 0;
}

函数重载

1. C语言是不支持同⼀作用域中出现同名函数的。 2. C++支持在同⼀作用域中出现同名函数,但是要求这些同名函数的形参不同,可以是参数个数不同或者参数类型不同。 3. 函数重载是通过函数参数类型、个数的不同来加以区分的。

代码语言:javascript
复制
#include<iostream>
using namespace std;

// 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(double left, double right)" << endl;
	return left + right;
}

// 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;
}

// 返回值不同不能作为重载条件,因为调⽤时⽆法通过参数区分
void fxx()
{}
int fxx()
{
    return 0;
}

// 下⾯两个函数构成重载
// 但是f()但是调⽤时,会报错,存在歧义,编译器不知道调⽤谁,可能是第一个函数没有参数,可能是第二个函数缺省参数。
void f1()
{
	cout << "f()" << endl;
}
void f1(int a = 10)
{
	cout << "f(int a)" << endl;
}

引用

引用的概念

1. 引用不是新定义⼀个变量,而是给已存在变量取了⼀个别名,编译器不会为引用变量开辟内存空间++2, 它和它引用的变量共用同⼀块内存空间。 2. 引用格式:类型& 引用别名 = 引用对象 。 3. 区别于 typedef 关键字,typedef 关键字用于给类型取别名,而引用用于给变量取别名。 4. 引用可以用来代替往函数里面传地址的操作。

代码语言:javascript
复制
#include<iostream>
using namespace std;
int main()
{
	int a = 0;
	// 引⽤:b和c是a的别名
	int& b = a;
	int& c = a;
	// 也可以给别名b取别名,d相当于还是a的别名
	int& d = b;
	++d;
	// 这⾥取地址我们看到是⼀样的
	cout << &a << endl;
	cout << &b << endl;
	cout << &c << endl;
	cout << &d << endl;
	return 0;
}
代码语言:javascript
复制
#include <iostream>
using namespace std;
void swap1(int x,int y)
{
	int mid;
	mid = y;
	y = x;
	x = mid;
}
void swap2(int& rx, int& ry)
{
	int mid;
	mid = ry;
	ry = rx;
	rx = mid;
}
int main()
{
	int a = 10;
	int b = 20;
	swap1(a, b);//没交换
	cout << a << " " << b << endl;
	swap2(a, b);//交换了
	cout << a << " " << b << endl;
	return 0;
}

引用的特性

1. 引用在定义时必须初始化,指针在引用时候不一定要初始化。 2. ⼀个变量可以有多个引用。 3. 引用⼀旦引用⼀个实体,再不能引用其他实体。 4. 由第三条特性可知:引用不能替代指针。(引用是用来辅助指针的)

代码语言:javascript
复制
#include <iostream>
using namespace std;
int main()
{
	int a = 10;
	int& b = a;//不能这样: int& b;
	int& c = b;
	cout << a << " " << b << " " << c << endl;//10 10 10
	int e = 20;
	b = e;//把e的值赋值给b(a)(c)
	cout << a << " " << b << " " << c << " " << e << endl;//20 20 20 20
	return 0;
}

引用的使用

1. 引用主要是于引用传参,改变引用对象(rx,ry)时同时改变被引用对象(x,y)。 2. 引用传参跟指针传参功能是类似的,引用传参相对更方便⼀些。(实现数据结构时候更容易理解)

代码语言:javascript
复制
#include <iostream>
using namespace std;
void Swap(int& rx, int& ry)
{
	int tmp = rx;
	rx = ry;
	ry = tmp;
}
int main()
{
	int x = 0, y = 1;
	cout << x << " " << y << endl;
	Swap(x, y);//改变引用对象(rx,ry)的同时,改变被引用对象(x,y)。
	cout << x << " " << y << endl;
	return 0;
}

const引用

1. const引用时候,权限不能放大,但是可以缩小。 2. 对变量进行赋值时,在计算中间值和类型转换中会产生临时对象,而C++规定临时对象具有常性,所以这里就触发了权限放大,必须要用const引用才可以。 3. 在自定义函数传参时候,可以利用const引用,这样实参的形式就更多样。

代码语言:javascript
复制
#include <iostream>
using namespace std;
int main()
{
	const int a = 10;
	int& ra = a;//报错,const引用权限不能放大,a已经被const引用限定,那么a的别名ra也必须被const引用限定
    const int& ra = a;//正确
	int b = 20;
    const int& rb = b;//不会报错,const引用权限可以缩小,b没有被const引用限定,那么b的别名rb可以被const引用限定
    int c = 30;
    int& rc = c * 3;//报错,引用过程中出现了计算中间值,需要用const引用
    const int& rc = c * 3;//正确
    return 0;
}
代码语言:javascript
复制
void Add(const int& ra)

Add(3);
Add(3*5);
Add(3.12);

指针与引用

1. 语法概念上,引用是⼀个变量的取别名,不开空间;指针是存储⼀个变量地址,要开空间。 2. 引用在定义时必须初始化;指针建议初始化,但是语法上不是必须的。 3. 引用在初始化时,引用⼀个对象后,就不能再引用其他对象;而指针可以在不断地改变指向对象。 4. 引用可以直接访问指向对象,指针需要解引用才是访问指向对象。 5. sizeof()中含义不同,引用结果为引用类型的大小,但指针始终是地址空间所占字节个数。 6. 指针很容易出现空指针和野指针的问题,引用很少出现,引用使用起来相对更安全⼀些。

inline修饰

1. 用inline修饰的函数叫做内联函数,编译时C++编译器会在调用的地方展开内联函数,这样调用内联函数就不需要建立栈帧了,就可以提高效率。 2. inline对于编译器而言只是⼀个建议,inline适用于频繁调用的短小函数,对于代码相对多⼀些的函数,加上inline也会被编译器忽略。 3. C语言宏的本质就是在编译的时候进行替换,C语言实现宏函数也会在预处理时替换展开,但是宏函数实现很复杂很容易出错的,且不方便调试。C++设计了inline目的就是替代C的宏函数。 4. inline修饰函数时不建议声明和定义分离到两个文件,分离会导致链接错误。因为inline被展开,就没有函数地址,链接时会出现报错。

代码语言:javascript
复制
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#define Add(x,y) ((x)+(y))
//后面不能加分号
//必须加外面的括号
//必须加里面的括号
using namespace std;
int main()
{
	int ret = Add(1, 2);
	cout << ret << endl;
	return 0;
}

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

nullptr修饰

1. 程序员通常会用0或NULL来表示一个空指针。但是这种方式存在一些潜在的问题,主要是因为0NULL实际上是整数常量,在某些情况下可能会导致意外的类型转换。 2. C++11中引入nullptr,nullptr是⼀个特殊的关键字,它可以转换成任意其他类型的指针类型。使用nullptr定义空指针可以避免类型转换的问题,因为nullptr只能被隐式地转换为指针类型,而不能被转换为整数类型。 3. nullptr提供了一种更安全、更清晰的方式来表示空指针,有助于减少错误并提高代码的可读性和安全性。

代码语言:javascript
复制
void f(int) 
{
    std::cout << "Integer version" << std::endl;
}

void f(char*) 
{
    std::cout << "Pointer version" << std::endl;
}

int main() 
{
    f(0);      // 可能调用f(int),也可能调用f(char*),取决于编译器
    f(nullptr); // 调用f(char*),因为nullptr明确指向一个指针
}

致谢

  感谢您花时间阅读这篇文章!如果您对本文有任何疑问、建议或是想要分享您的看法,请不要犹豫,在评论区留下您的宝贵意见。每一次互动都是我前进的动力,您的支持是我最大的鼓励。期待与您的交流,让我们共同成长,探索技术世界的无限可能!

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2024-11-07,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • C++的兼容性
  • 命名空间
    • namespace关键字
      • 命名空间的使用
      • C++的输入输出
      • 缺省参数
      • 函数重载
      • 引用
        • 引用的概念
          • 引用的特性
            • 引用的使用
            • const引用
            • 指针与引用
            • inline修饰
            • nullptr修饰
            • 致谢
            领券
            问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档