C++入门学习

目录

C++入门

1.namespace命名空间

2.C++的输入与输出

3.缺省参数

4.函数重载

5.引用

5.1引用和指针的关系

6.inline函数

7.nullptr


C++入门

C++兼容C语言的大多数语法,在C语言中我们的第一个程序是这样写的

cpp 复制代码
#include<stdio.h>
int main() {

	printf("hello world");

	return 0;
}

在C++中这样的语法仍然是可以运行的,但是在C++中我们这样写,使用cout进行输出,这样写的好处是我们不需要写输出的占位符类型,会自动识别类型进行输出,所以在自定义类型的输出上会方便很多

cpp 复制代码
#include<iostream>
using namespace std;
int main() {

	cout << "hello world" << endl;

	return 0;
}

1.namespace命名空间

在C++中有四个域,全局域,局部域,命名空间域,类域

命名空间域就是namespace,在这里定义的变量以及函数和其他域可以很好地分离开,避免命名冲突等问题,而在正常情况下,编译器查找一个变量时,默认只会在局部域和全局域进行查找,所以如果要使用命名空间内部的变量,我们有三种使用方法

指定命名空间访问:命名空间::

将某个成员展开:using 命名空间::变量;

展开整个命名空间:using namespace 命名空间;

第一种方法是最推荐使用的,对于命名空间内部的变量以及函数,直接指定命名空间去访问

例如我们现在将一块空间命名为test,在其中定义x以及一个add函数,在输出的时候,如果没有指定命名空间,那么只在全局域以及局部域去查找变量,所以第一个x会输出10,第二个x因为指定了test这个命名空间,那么会输出1,add函数因为在命名空间域内,所以也需要指定命名空间访问

cpp 复制代码
#include<iostream>
using namespace std;

namespace test {
	int x = 1;
	int add(int x, int y) {
		return x + y;
	}
}
int main() {
	int x = 10;
	cout << x << endl;
	cout << test::x << endl;
	cout << test::add(1, 2) << endl;

	return 0;
}

2.C++的输入与输出

C++的标准库都放在一个叫做std的命名空间中,我们再练习写代码的时候,可以直接将std展开,这样使用内部函数就很方便

头文件<iostream>,定义了标准的输入和输出对象

std::cin是istream类的对象,主要面向窄字符的标准输入

std::cout是ostream类的对象,主要面对窄字符的标准输出

std::endl相当于一个换行符的作用,并刷新缓存区

<<是流插入运算符,>>是流提取运算符

使用C++在输入输出时会更加方便,不需要手动指定输入输出的格式,C++的输入输出可以自动识别变量类型,且更好地支持定义类型对象的输入和输出

cpp 复制代码
#include<iostream>
using namespace std;

int main() {
	int a = 0;
	int b = 0;
	cin >> a >> b;
	cout << a << b << endl;
	return 0;
}

3.缺省参数

我们在定义函数时,可以将某些参数指定一个缺省值,表示如果使用函数时没有给该参数手动赋值,那么就会调用这个缺省值进行操作

缺省分为全缺省和半缺省,全缺省就是全部变量都给上缺省值,半缺省就是只给一部分变量给缺省值,另一部分仍然需要使用函数时手动赋值

C++规定半缺省的缺省参数必须从右往左依次给,不可以跳跃 ,同样的在给实参的时候也必须从左往右给,不可以跳跃给实参,这样才不会出现歧义,不知道哪个实参赋值给了哪个形参

例如下面的代码,从右往左给了两个形参缺省值,在使用函数时,从左往右给了三个实参,既然有了实参,那么变量z就不会使用缺省值10,而是使用给的实参3

cpp 复制代码
#include<iostream>
using namespace std;

int add(int x, int y, int z = 10, int k = 20) {
	return x + y + z + k;
}
int main() {
	cout << add(1, 2, 3) << endl;
	return 0;
}

注意一点,如果函数的声明和定义是分离的话,缺省值只能给在函数声明的位置

4.函数重载

C++允许同一个作用域中出现同名函数,只要这些函数的形参不同就可以构成重载,不管是参数个数不同还是参数类型不同,表现出了多态行为

例如以下add函数,都构成函数重载,在使用时会根据传入参数的类型个数顺序等不同,自动调用对应的函数,使用起来比较方便

cpp 复制代码
int add(int x, int y) {
	return x + y;
}

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

int add(int x, int y, int z) {
	return x + y + z;
}

double add(int x, double y) {
	return x + y;
}

5.引用

引用与C语言经常使用的指针的作用基本是一致的

引用的写法:类型& = 引用对象

例如我们定义一个整型变量b,使用c和d引用它,相当于给b取了别名,在使用c和d的时候本质上就是在使用b这个变量,所以直接对c和d修改值,可以更改b的值

注意一个别名只能引用一个实体变量,但是一个变量可以有多个引用

所以这里将d赋值为y,是将y的值赋值给它,而不是d改变了引用对象

引用的使用本质是为了简化程序,避开复杂的指针,因为取别名更容易理解,例如张三,它的外号叫小张,我们说小张在写代码,本质上就是张三这个人在写代码,直接对我们需要使用的那个变量进行操作,比用指针取地址操作更加简便

cpp 复制代码
int b = 10;
int& c = b;
int& d = b;

cout << b << endl;
cout << c << endl;
cout << d << endl;
int y = 20;
d = y;//只能引用一个实体,所以这里d依旧是b的别名
//将值更改为20
cout << b << endl;
cout << c << endl;
cout << d << endl;

回想一下之前使用C语言写栈,调用初始化函数和入栈函数等操作时,我们需要传入栈的地址,也就是&stack去操作,但是如果我们将传入的形参改为引用,相当于对这个栈取别名,可以直接用这个别名进行操作,而且代码可读性更高

例如下面这段代码,定义了一个叫st的栈,然后有三个函数,原本我们在函数形参这一块使用的是指针类型的传入,然后用指针去操作栈内的变量,现在直接使用引用的操作,相当于直接对栈本身进行操作,函数写起来更简便,而且使用函数时很直观的知道是在对栈进行什么操作,可以直接传入栈,而不是传入一个地址

cpp 复制代码
typedef int StackDataTpye;
struct StackNode {
	StackDataTpye* arr;
	int top;//栈顶
	int space;//空间大小
};
typedef struct StackNode SN;

void StackInit(SN& stack) {
	//初始化栈
	stack.arr = (StackDataTpye*)malloc(2 * sizeof(StackDataTpye));
	//初始先申请两个空间
	if (stack.arr == NULL) {
		perror("malloc");
		return;
	}
	//将栈顶置为0
	//空间大小置为2
	stack.top = 0;
	stack.space = 2;
}
void StackPush(SN& st, StackDataTpye x) {
	assert(st.arr != nullptr);
	if (st.top == st.space) {
		//如果栈顶到达空间上限时
		//对数组进行扩容
		StackDataTpye* p1 = (StackDataTpye*)realloc(st.arr, 2 * (st.space) * sizeof(StackDataTpye));
		if (p1 == NULL) {
			perror("realloc");
			return;
		}
		st.arr = p1;
		st.space *= 2;//进行二倍扩容
	}
	//进行入栈操作,top++ 
	st.arr[st.top] = x;
	st.top++;

}

void PrintStack(SN& st) {
	//遍历栈进行打印
	assert(st.arr != nullptr);
	for (int i = 0; i < st.top; i++) {
		printf("%d ", st.arr[i]);
	}
	printf("\n");
}


SN st;
StackInit(st);
StackPush(st, 1);
StackPush(st, 2);
StackPush(st, 3);
PrintStack(st);

注意一下const引用的操作,对于一些临时对象和本身就是const修饰的变量的引用,需要使用const引用,因为引用可以进行权限的缩小,但是不可以进行权限的放大

例如对于const修饰的变量a,用int&进行引用,是一种权限的放大,本来a的值是不允许修改的,但是c引用a之后,对a的值有修改的风险,所以不可以这样写,但是权限的缩小是可以的,我们可以直接修改bb的值为20,注意这里的const作用是防止通过该引用修改bb的值,而不是锁定了dd的值为bb修改前的10,所以在bb修改为20之后,dd的值也是20,只不过不能通过直接更改dd的值来修改bb,这样写是会报错的

看第三个例子,yy*3是一个临时对象,而C++中临时对象具有常性,是不可以被修改的,所以如果用int&直接去引用,触发了权限放大,所以对x++操作时,相当于对一个常量进行修改,会报错

cpp 复制代码
const int a = 1;
int& c = a;
//这样是会报错的,因为const修饰的a值不可更改
//int& c是对权限的放大,所以不对
//可以进行权限的缩小,但是不能进行权限的放大

int bb = 10;
const int& dd = bb;
//这样进行权限缩小的引用是可以的
bb = 20;
dd = 30;
//这样是不能修改的,因为dd是const修饰
//所以当bb用别名dd时不能修改
//但是仍然可以通过使用bb变量名修改bb本身的值

int yy=10;
int& xx=yy*3;
xx++;
//在C++中,一个通过表达式产生的值是需要临时拷贝的,叫做临时对象
//这个拷贝出来的值具有常性,不可以更改
//相当于对于yy*3的拷贝值,使用了const修饰
//此时用xx给他取别名,没有用const触发了权限放大
//所以会产生报错,因为拷贝值具有常性

5.1引用和指针的关系

引用作为变量的别名,不开辟空间,指针要存储变量的地址,需要开辟一块空间存储

引用在定义时必须初始化,指针只是建议初始化

引用在初始化引用一个对象之后,就不能更改了,而指针可以不断改变指向的对象

引用是直接访问引用的对象,而指针解引用之后才是指向的对象

引用结果的大小为引用对象的大小,指针的大小取决于环境是32位还是64位

指针容易出现空指针和野指针的问题,引用相对安全一点

6.inline函数

C++设计inline函数的目的就是替代C语言的宏函数,因为宏函数在预处理展开替换时,会由于优先级的问题导致结果不正确,因为宏函数是直接进行替换,而且不方便进行调试

对于内联函数来说,不支持声明与定义分开 ,直接将定义写在需要使用他的文件下,因为inline函数使用时,是一种替换,不会产生链接的操作,使用call去开辟函数栈帧,所以如果分离声明与定义,会由于找不到链接地址而报错,inline函数的使用只是对编译器的建议,递归函数以及代码量大的函数即使加上inline,编译器仍然不会直接展开函数,而是call调用,代码量小且使用频繁的函数可以用inline函数

如果想知道内联函数在运行时是否展开还是直接call调用,可通过调试,打开反汇编,运行到该函数的地方时,如果使用call说明没有展开,反之则进行了展开,例如以下函数,虽然是内联函数,但是实际使用时还是再用call,所以inline对于编译器只是一个建议

7.nullptr

在C语言中我们用NULL表示指针为空,表示无类型指针void*的常量,但是在C++中,NULL可能被定义为常量0,所以如果用NULL作为函数实参传入时,如果有两个函数,一个需要传入一个指针,另一个需要传入整型变量,但是此时我们用NULL作为实参,肯定是希望调用第一个函数,但是由于NULL可能被解释为0,那么就会调用第二个函数,背离了预期

所以在C++引入了nullptr,它是一个特殊关键字,可以转换为任意其他类型的指针类型,所以使用nullptr就可以避免这种问题,因为nullptr只能被隐式转换为指针类型,而无法被转换成整数类型

相关推荐
吃不饱的得可可2 小时前
protobuf万字总结(C++)
开发语言·c++
m0_662577972 小时前
嵌入式C++安全编码
开发语言·c++·算法
2301_810160952 小时前
代码生成器优化策略
开发语言·c++·算法
HUTAC2 小时前
关于进制转换及其应用的算法题总结
数据结构·c++·算法
SPC的存折2 小时前
Python3编程之python基础
开发语言·python
暮冬-  Gentle°2 小时前
C++中的工厂模式实战
开发语言·c++·算法
Lisssaa2 小时前
打卡第二十二天
c++·算法·图论
pu_taoc2 小时前
理解 lock_guard, unique_lock 与 shared_lock 的设计哲学与应用场景
开发语言·c++·算法
NGC_66112 小时前
Java 死锁预防:从原理到实战,彻底规避并发陷阱
java·开发语言