c++入门篇

    • 1、c++关键字
    • 2、命名空间
      • 2.1命名冲突
      • 常见场景
      • 2.2命名空间
      • 命名空间的定义
      • 命名空间的使用
    • 3、c++中的输入与输出
    • 4、缺省函数
    • 函数重载
      • 5.1参数类型不同
      • 参数个数不同
      • 参数顺序不同
      • 5.4为什么c++支持函数重载
    • 6、引用
      • 6.1引用规则
      • 6.2常饮用
      • 引用的使用场景
        • 6.3.1做参数
        • 6.3.2做返回值
      • 6.4引用和指针的区别
    • 7、内联函数
    • 8、auto关键字
      • 8.1 auto的使用
      • 8.2 auto不能推导的场景
    • 9、基于范围for的循环(c++11)
    • 10、指针空值nullptr(c++11)

在这里插入图片描述

1、c++关键字

在这里插入图片描述
c++总计63个关键字,C语言总计32个关键字。

2、命名空间

2.1命名冲突

命名冲突:命名冲突是指在程序中使用相同的标识符(如变量名、函数名等)时导致的问题。

常见场景

1、与库中的发生冲突。
2、文件之间发生冲突。

举个栗子:
在这里插入图片描述
这里的变量rand是局部变量,处于main函数的域中,故不与stdlib.h库中的rand函数发生命名冲突的问题。
在这里插入图片描述
在这里插入图片描述
这里定义了一个全局变量rand与引入的stdlib.h库中的rand函数出现了命名冲突。

在C语言中这个问题无法有效解决,只能要求程序员遵循良好的命名约定才可以,如果在一个大型项目中,定义的变量、函数和类都是大量存在的,这些变量、函数和类的名称将都存在于全局作用域中,这会导致命名冲突经常发生,所以c++引入了一个命名空间的概念。

2.2命名空间

概念:是一种用来组织代码、避免命名冲突并提供作用域限定的机制。它允许开发者将代码划分为逻辑上独立、可重用的区域。

命名空间的定义

namespace(关键字) + 命名空间名字 +{ },{ }中即为命名空间的成员。

namespace yueyue
{
    //在命名空间中可以定义变量/函数/类型
    //变量
    int sum=0;
    //函数
    int add(int a,int b)
    {
      return a+b;
    }
    //类型
    struct Seqlist
    {
      int *a;
      int size;
      int capacity;
    }
}

在一些极端情况下,命名空间中还可以嵌套命名空间。

namespace yueyue
{
  int sum=0;
  namespace room
  {
    int sum=1;
  }
}

在不同的文件中定义命名空间名字相同的情况时,链接时会将它们合并成同一个命名空间。

命名空间的使用

没有指定时,有指定时,::域作用限定符。

1、加命名空间名称及作用域限定符

int main()
{
  printf("%d",yueyue::sun);
  //这里我们指定变量sum是yueyue命名空间中的sum变量。
  return 0;
}

2、使用using将命名空间中某个成员引入

using namespace yueyue::sum
//我们引入yueyue命名空间单独将sum暴露出来
int main()
{
  printf("%d",sun);
  //这里我们就不需要指定命名空间了。
  return 0;
}

3、使用using namespace 命名空间名称引入

using namespace yueyue
//我们引入yueyue命名空间全部展开
int main()
{
  printf("%d",sum);
  return 0;
}

总结 :一个命名空间就定义了一个新的作用域,命名空间中的所有内容都局限于该命名空间中,相当于围墙,我们需要域作用限定符作为钥匙来使用命名空间里的成员,或者直接展开命名空间(但是这种行为比较危险:会取消了作用域限定,增加了出现命名冲突的风险。)

3、c++中的输入与输出

#include<iostream>
int main()
{
  //输出
  std::cout<<"hello c++"<<std::endl;
  //输入
  int count =0;
  std::cin>>count;
  std::cout<<count<<std::endl;
  return 0;
 }

endl是特殊的C++符号,表示换行输出
<<是流插入运算符,>>是流提取运算符。

解析:
#include将 C++ 标准库中的输入输出流相关的类和函数引入了程序。包括了 std::cout、std::cin、std::endl 等。如果我们要使用iostream库中cout,cin函数我们就需要std::cout、std::cin。
如果我们嫌弃这样写过于繁琐我们可以std命名空间全部展开。

using namespace std;

但是因为它可能引入了整个 std 命名空间,可能会导致潜在的命名冲突或者使得代码不够清晰,我们常常倾向于在局部作用域中使用 using 声明,只引入需要的特定标识符。

#include<iostream>
using std::cout;
using std::cin;
int main()
{
  //输出
  cout<<"hello c++"<<endl;
  //输入
  int count =0;
  cin>>count;
  cout<<count<<endl;
  return 0;
 }

在一般的小型项目中,我们可以将std命名空间全部展开。

4、缺省函数

概念:函数的默认参数是一种允许你为函数的某些参数指定默认值的特性。当调用函数时,如果没有提供相应的参数,那么将使用默认值。这样可以使函数调用更加灵活,而不必每次都提供所有参数。

#include<iostream>
using namespace std;
int sum(int a=1,int b=2,int c=3)
{
  return a+b+c;
}
int main()
{
  cout<<sum()<<endl;
  return 0;
}
 

运行结果:
在这里插入图片描述
全缺省函数

using namespace std;
int sum(int a=1,int b=2,int c=3)
{
  return a+b+c;
}

半缺省函数

using namespace std;
int sum(int a,int b=2,int c=3)
{
  return a+b+c;
}

默认参数的规则:
1、默认参数只能从右向左连续出现。

2、默认参数在函数声明中指定。
3. 缺省值必须是常量或者全局变量。
关于规则2,这是因为缺省值在声明与定义位置同时出现,恰巧两个位置提供的值不同,那编译器就无法确定到底该用那个缺省值,并且函数声明提供了函数的接口,所以我们将默认参数在函数声明中指定.

//a.h 函数声明
void Func(int a = 10);
//a.cpp 函数定义
void Func(int a = 20)
{}

函数重载

概念:函数重载是指在同一个作用域内,可以有多个函数具有相同的名称,但它们的参数列表必须不同(包括参数的类型、个数、顺序等)。C++中允许函数通过参数的不同来区分彼此,从而支持函数重载。

5.1参数类型不同

#include<iostream>
using namespace std;
int sum(int a, int b)
{
  cout << "int sum(int a, int b)" << endl;
  return a + b;
}
double sum(double a, double b)
{
  cout << "double sum(double a, double b)" << endl;
  return a + b;
}
int main()
{
  cout<<sum(1,1)<<endl;
  cout<<sum(1.1,1.1)<<endl;
  return 0;
}

运行结果:
在这里插入图片描述

参数个数不同

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

运行结果:
在这里插入图片描述

参数顺序不同

#include<iostream>
using namespace std;
double sum(int a, double b)
{
  cout << "double sum(int a, double b)" << endl;
  return a + b;
}
double sum(double a, int b)
{
  cout << "double sum(double a, int b)" << endl;
  return a + b;
}
int main()
{
  cout<<sum(1,11)<<endl;
  cout<<sum(1.1,1)<<endl;
  return 0;
}

运行结果:
在这里插入图片描述

5.4为什么c++支持函数重载

在C/C++中,一个程序要运行起来,需要经历以下几个阶段:预处理、编译、汇编、链接。

简单来讲就是,函数名在编译过程中的转换通常涉及到函数名修饰规则,也被称为名称修饰,而在链接时,编译器需要通过这种函数名修饰规则,链接函数,每个编译器的函数名修饰规则不同。

1、预处理阶段:在这个阶段,通常不涉及函数名的修饰,因为预处理阶段主要关注源代码的文本替换和处理。
2、编译阶段:在C++中,函数名会被修饰以包含关于函数参数类型和数量的信息。这是为了支持函数重载。不同的编译器可能有不同的修饰规则。
3、汇编阶段:汇编阶段会使用已经被修饰的函数名,将其转换为对应的机器代码,并为每个函数分配一个地址。
4、在链接阶段,函数名的修饰主要用于解决函数调用的地址。确保调用的函数存在,而且与调用的地方匹配。

C语言与C++的函数名修饰比较:
C语言: 不涉及函数名的修饰,通常函数名直接映射到目标文件中的符号,例如我们定义两个ADD函数无论是参数的个数类型等等不同,函数名的修饰直接就是ADD,这样编译器就不知道链接哪一个了。
C++: 函数名被修饰以包含关于函数参数类型和数量的信息,以支持函数重载,例如我们定义两个ADD函数,编译器会根据函数参数的个数类型等等不同,所修饰出的函数名也就不同,这样即使函数名相同,但是编译器链接时是根据修饰后的函数名链接的。

6、引用

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

类型& 引用变量名(对象名) = 引用实体;

举个栗子:

#include<iostream>
using namespace std;
int main()
{
  int a=0;
  int &b=a;
  b++;
  cout<<a<<endl;
  cout<<&a<<endl;
  cout<<&b<<endl;
  return 0;
}

运行结果:
在这里插入图片描述

共用同一块空间,有多个名字.

6.1引用规则

  1. 引用在定义时必须初始化
  2. 一个变量可以有多个引用
  3. 引用一旦引用一个实体,再不能引用其他实体

6.2常饮用

错误示例权限的放大:

#include<iostream>
using namespace std;
int main()
{
  const int a=0;
  int &b=a;
  return 0;
}

变量a被const修饰,权限为可读。
给a取别名b,但是b的权限是可读可写。
a权限仅为可读,而作为它的的别名b,又怎能可读可写呢?这是属于权限的放大。

修正:
这时候就处于权限的平移了。

#include<iostream>
using namespace std;
int main()
{
  const int a=0;
  const int &b=a;
  return 0;
}

权限的缩小:

#include<iostream>
using namespace std;
int main()
{
  int a=0;
  const int &b=a;
  return 0;
}

总结:权限可以平移,缩小但是不可以放大。

引用的使用场景

6.3.1做参数

在C语言中,我们写一个交换函数是这样的。

void Swap(int* left, int* right)
{
  int temp = *left;
  *left = *right;
  *right = temp;
}

在c++,我们写一个交换函数是这样的。

void Swap(int& left, int& right)
{
  int temp = left;
  left = right;
  right = temp;
}

通过引用传递参数,可以直接修改实参的值。
可以避免值传递的开销,同时保持对实参的直接访问。

6.3.2做返回值

传值返回:


#include<iostream>
using namespace std;
int count()
{
  int x=0;
  x++;
  return x;
}
int main()
{
  int ret=count();
  cout<<ret<<endl;
  return 0;
}

当我们调用count函数,返回x时,因为局部变量x是属于count函数的作用域的,除了作用域变量x会销毁,这时编译器会生成一个临时变量来接收x的值,作为返回值给变量ret。

但是传值返回需要拷贝,效率降低,为了提高效率,我们使用传引用返回试试。

#include<iostream>
using namespace std;
int& count()
{
  int x=0;
  x++;
  return x;
}
int main()
{
  int ret=count();
  cout<<ret<<endl;
  return 0;
}

这里我们使用传引用返回时,没有进行临时变量的拷贝提高了效率,但是这种做法是错误的,我们返回变量x的别名让变量ret来接收,count()函数这时所开辟的空间已经被操作系统回收了。打印ret的值要取决于编译器是否清理了那段栈帧。

所以返回的引用指向全局变量或静态变量,这些变量在程序的整个运行周期内都是有效的,因此引用也是有效的。

#include<iostream>
using namespace std;
int& count()
{
  static int x=0;
  x++;
  return x;
}
int main()
{
  int ret=count();
  cout<<ret<<endl;
  return 0;
}

6.4引用和指针的区别

在这里插入图片描述
在底层实现上实际是有空间的,所以在底层引用是按照指针方式来实现的。

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

7、内联函数

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

int Add(int a, int b)
{
	return a + b;
}
int main()
{
  	int ret = Add(1, 1);
  	cout<<ret<<endl;
  	return 0;
}

在这里插入图片描述

inline int Add(int a, int b)
{
	return a + b;
}
int main()
{
  	int ret = Add(1, 1);
  	cout<<ret<<endl;
  	return 0;
}

在这里插入图片描述
特性:

1、 inline是一种以空间换时间的做法,如果编译器将函数当成内联函数处理,在编译阶段,会用函数体替换函数调用,缺陷:可能会使目标文件变大,优势:少了调用开销,提高程序运行效率。
2、inline对于编译器而言只是一个建议,不同编译器关于inline实现机制可能不同,一般建议:将函数规模较小(即函数不是很长,具体没有准确的说法,取决于编译器内部实现)、不是递归、且频繁调用的函数采用inline修饰,否则编译器会忽略inline特性。
3、inline不建议声明和定义分离,分离会导致链接错误。因为inline被展开,就没有函数地址了,链接就会找不到

8、auto关键字

auto关键字允许编译器自动推导变量的类型。使用 auto 关键字声明变量时,编译器会根据变量的初始化表达式推断出其类型,从而简化代码并提高可读性。

#include<iostream>
using namespace std;
int Test()
{
	return 10;
}
int main()
{
	int a = 10;
	auto b = a;
	auto c = 'b';
	auto d = Test();
	cout << typeid(b).name() << endl;
	cout << typeid(c).name() << endl;
	cout << typeid(d).name() << endl;
	return 0;
}

运行结果:
在这里插入图片描述

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

8.1 auto的使用

1.auto与指针和引用结合起来使用

int main()
{
	int x = 10;
	auto a = &x;
	auto* b = &x;
	auto& c = x;
	cout << typeid(a).name() << endl;
	cout << typeid(b).name() << endl;
	cout << typeid(c).name() << endl;
	return 0;
}

用auto声明指针类型时,用auto和auto*没有任何区别,但用auto声明引用类型时则必须加&

2.在同一行定义多个变量

oid Test()
{
    auto a = 1, b = 2; 
    auto c = 3, d = 4.0;  
    // 编译失败,c和d的初始化表达式类型不同
}

当在同一行声明多个变量时,这些变量必须是相同的类型,否则编译器将会报错,因为编译器实际只对第一个类型进行推导,然后用推导出来的类型定义其他变量。

8.2 auto不能推导的场景

1.auto不能作为函数的参数

void TestAuto(auto a)
{}
//在进行编译链接时,编译器对于参与无法推导。

2.auto不能直接用来声明数组

void Test()
{
    int a[] = {1,2,3};
    auto b[] = {456};
}

9、基于范围for的循环(c++11)

在C语言中我们遍历一个数组时,是这样的。

#include<iostream>
using namespace std;

int main()
{
	int arr[] = { 1, 2, 3, 4, 5 };
	for (int i = 0; i < sizeof(arr) / sizeof(arr[0]); ++i)
		cout << arr[i];
	cout << endl;
	for (int* p = arr; p < arr + sizeof(arr) / sizeof(arr[0]); ++p)
		cout << *p ;
	cout << endl;
	return 0;
}

在这里插入图片描述
在c++中我们可以这样。

#include<iostream>
using namespace std;

int main()
{
	int arr[] = { 1, 2, 3, 4, 5 };
	for (auto e : arr)
		cout << e << " ";
	return 0
}

for循环后的括号由冒号“ :”分为两部分:第一部分是范围内用于迭代的变量,第二部分则表示被迭代的范围。

e 是范围for 循环中的局部变量,其类型由编译器根据数组元素类型进行推导,并且在每次循环迭代中被自动声明和初始化。

如果我们想要改变数组中的数值,可以这样。

#include<iostream>
using namespace std;

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

运行结果:
在这里插入图片描述

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

范围for的使用条件

for循环迭代的范围必须是确定的对于数组而言,就是数组中第一个元素和最后一个元素的范围.
迭代的对象要实现++和==的操作。

10、指针空值nullptr(c++11)

在C语言中我们对一个指针初始化赋值一个空指针时.

int* p=NULL;

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

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

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

举个栗子:

#include<iostream>
using namespace std;
void fun(int)
{
	cout << "fun(int)" << endl;
}
void fun(int*)
{
	cout << "fun(int*)" << endl;
}
int main()
{
	fun(0);
	fun(NULL);
	fun((int*)NULL);
	return 0;
}


运行结果:
在这里插入图片描述
好了,关于c++入门篇就到这里了,如果文章中有错误请在评论区中指出哦.

评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值