C++入门

目录

一、C++和C语言的关系

二、C++ 命名空间--namespace 

   1、 namespace的引入

   2、命名空间的定义及使用

      2.1 命名空间中不仅可以定义变量,还可以定义函数,类型。

      2.2 命名空间还可以嵌套

      2.3 同一个工程中允许存在多个相同名称的命名空间,编译器最后会合成同一个命名空间中。

 三、C++输入和输出

 四、缺省参数

        1、缺省参数的概念

        2、缺省参数的分类

                 2.1 全缺省参数

                2.2 半缺省参数

五、函数重载 

        1、函数重载概念

                 1.1 参数类型不同

                1.2 参数个数不同 

                1.3 参数类型顺序不同(顺序不同是指形参类型的顺序不同)

六、引用

        1.引用的概念

        2.引用特性

        3.常引用 

        4.引用的使用场景

                4.1 做参数

                4.2 做返回值

         5、传值、传引用效率比较

        6、引用和指针的区别

七、内联函数 

        1、概念

2、特性 

八、auto关键字(C++11)  

        1、简述

        2、auto的使用细则 

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

                2.2 在同一行定义多个变量

        3、auto不能推导的场景 

            3.1 auto不能作为函数的参数

            3.2 auto不能直接用来声明数组

          3.3 其他情况

九、基于范围的for循环(C++11)  

        1、 范围for的语法

        2、范围for的使用条件 

            2.2 迭代的对象要实现++和==的操作 

十、指针空值nullptr(C++11) 

       C++98中的指针空值 NULL


一、C++和C语言的关系

        C++是在C的基础之上,引入了面向对象的编程思想,并增加了许多有用的库,以及编程范式 ,补充C语言语法的不足,并对C语言设计不合理的地方进行优化。

二、C++ 命名空间--namespace 

   1、 namespace的引入

       在我们学习C语言的过程中,我们可能遇到过如下问题: 

#include <stdio.h>
#include <stdlib.h>

int rand = 10;

int main()
{
	printf("%d\n", rand);
	return 0;
}

 这段代码看着好像并没有任何错误,但是一运行我们就会发现会报错“rand”重定义。

     那么遇到类似这样的问题我们应该如何解决呢?

     在C语言中我们没有办法解决类似的命名冲突问题,所以在C++中提出了命名空间 namespace来解决这一问题。

   2、命名空间的定义及使用

定义命名空间,需要使用到namespace关键字,后面跟命名空间的名字,然后接一对{}即可,{} 中即为命名空间的成员。
namespace name
{
    // 命名空间的成员
}

使用命名空间,命名空间的使用有三种方式:

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

namesapce T
{
    int a = 1;
}
int main()
{
    printf("%d\n", T::a);//注意::这个就是作用域限定符
    return 0;    
}

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

namespace T
{
    int b = 9;
}
using T::b;
int main()
{
    printf("%d\n", b);
    return 0;    
}

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

namespace T
{
	int a = 1;
	int b = 9;
}
using namespace T;
int main()
{
	printf("%d\n", a);
	printf("%d\n", b);
	return 0;
}
      2.1 命名空间中不仅可以定义变量,还可以定义函数,类型。
//命名空间中可以定义变量/函数/类型
 namespace T1
{
	int rand = 10;

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

	struct Node
	{
		struct Node* next;
		//...
	};
}
int main()
{
	printf("%d\n", T1::rand);//这样一来就解决了上面rand重定义的问题了
	printf("%d\n", T1::Add(3, 5));
	struct T1::Node node;
}
      2.2 命名空间还可以嵌套
//命名空间嵌套
namespace T1
{
	int Add(int left, int right)
	{
		return left + right;
	}
	namespace T2
	{
		int Sub(int left, int right)
		{
			return left - right;
		}
	}
}
int main()
{
	printf("%d\n", T1::Add(3, 5));
	printf("%d\n", T1::T2::Sub(3, 5));
}
      2.3 同一个工程中允许存在多个相同名称的命名空间,编译器最后会合成同一个命名空间中。
//Test.h
namespace N1
{
	int Mul(int left, int right);

	int Sub(int left, int right)
	{
		return left - right;
	}
}

//Test.cpp
namespace N1
{
	int Add(int left, int right)
	{
		return left + right;
	}
	int Mul(int left, int right)
	{
		return left * right;
	}
}

int main()
{
	printf("%d\n", N1::Add(1, 2));
	printf("%d\n", N1::Sub(6, 3));
	printf("%d\n", N1::Mul(2, 3));
}

注意:一个命名空间就定义了一个新的作用域,命名空间中的所有内容都局限于该命名空间中

 三、C++输入和输出

#include <iostream>
using namespace std;//std是C++标准库的命名空间名

int main()
{
	cout << "Hello World!" << endl;
	return 0;
}

注意:

        1. 使用cout标准输出对象(控制台)和cin标准输入对象(键盘 )时,必须包含< iostream >头文件
以及按命名空间使用方法使用std;cout和cin是全局的流对象,endl是特殊的C++符号,表示换行输出,他们都包含在包含<iostream >头文件中。
       2. << 是流插入运算符, >> 是流提取运算符。
cin和cout可以自动识别变量的类型
   

 四、缺省参数

        1、缺省参数的概念

        缺省参数是声明或定义函数时为函数的参数指定一个缺省值。在调用该函数时,如果没有指定实参则采用该形参的缺省值,否则使用指定的实参。

    

        2、缺省参数的分类

                 2.1 全缺省参数

             

                2.2 半缺省参数

             

  注意:

      1. 半缺省参数必须从右往左依次来给出,不能间隔着给
      2. 缺省参数不能在函数声明和定义中同时出现
      3. 缺省值必须是常量或者全局变量
      4. C语言不支持(编译器不支持)

 注意:如果声明与定义位置同时出现,恰巧两个位置提供的值不同,那编译器就无法确定到底该用那个缺省值。

五、函数重载 

        1、函数重载概念

函数重载:是函数的一种特殊情况,C++允许在同一作用域中声明几个功能类似的同名函数,这些同名函数的形参列表(参数个数 或 类型 或 类型顺序)不同
                 1.1 参数类型不同
int Add(int left, int right)
{
	cout << "int Add(int left, int right)" << endl;
	return left + right;
}
double Add(double left, idouble right)
{
	cout << "double Add(double left, idouble right)" << endl;
	return left + right;
}
int main()
{
	Add(1, 2);
	Add(1.0, 2.0);
	return 0;
}

     

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

int main()
{
	f();
	f(1);
	return 0;
}

                1.3 参数类型顺序不同(顺序不同是指形参类型的顺序不同)
// 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()
{
	f(3, 'e');
	f('e', 3);
	return 0;
}

         

这里需要注意的是:构成重载,在不传参的时候调用会存在二义性(看下面代码)

  

六、引用

        1.引用的概念

引用不是新定义一个变量,而是给已存在变量取了一个别名,编译器不会为引用变量开辟内存空间,它和它引用的变量共用同一块内存空间。
基本语法 : 类型 & 引用变量名 ( 对象名 ) = 引用实体
注意:引用类型必须和引用实体同种类型

      

        2.引用特性

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

 

        3.常引用 

void Test()
{
    const int a = 10;
    //int& ra = a;   // 该语句编译时会出错,a为常量
    const int& ra = a;
    // int& b = 10; // 该语句编译时会出错,b为常量
    const int& b = 10;
    double d = 12.34;
    //int& rd = d; // 该语句编译时会出错,类型不同
    const double& rd = d;
}

        4.引用的使用场景

                4.1 做参数
//做参数
//void Swap(int* left, int* right)
//{
//	int temp = *left;
//	*left = *right;
///	*right = temp;
//}
void Swap(int& left, int& right)
{
	int temp = left;
	left = right;
	right = temp;
}
int main()
{
	int a = 4;
	int b = 9;
	//Swap(&a,&b);
	Swap(a, b);
	return 0;
}
                4.2 做返回值
int& Add(int a, int b)
{
    int c = a + b;
    return c;
}
注意: 如果函数返回时,出了函数作用域,如果返回对象还在 ( 还没还给系统 ) ,则可以使用
引用返回,如果已经还给系统了,则必须使用传值返回。(观察下面代码看看结果是什么?为什么?)
int& Add(int a, int b)
{
    int c = a + b;
    return c;
}
int main()
{
    int& ret = Add(1, 2);
    Add(3, 4);
    cout << "Add(1, 2) is :"<< ret <<endl;
    return 0;
}

注意:
  1、函数运行时,系统需要给该函数开辟独立的栈空间,用来保存该函数的形参、局部变量以及一些寄存器信息等
  2、函数运行结束后,该函数对那个的栈空间就被系统回收了
  3、空间被回收指该块栈空间暂时不能使用,但是内存还在

         5、传值、传引用效率比较

        6、引用和指针的区别

语法概念上引用就是一个别名,没有独立空间,和其引用实体共用同一块空间。
底层实现上实际是有空间的,因为引用是按照指针方式来实现的。

观察引用和指针汇编代码的区别: 

  

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

七、内联函数 

        1、概念

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

查看方式:
1. release 模式下,查看编译器生成的汇编代码中是否存在 call Add
2. debug 模式下,需要对编译器进行设置,否则不会展开 ( 因为 debug 模式下,编译器默认不会对代码进行优化。

 

2、特性 

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

// Test.cpp
#include "Test.h"
void func(int i)
{
 cout << i << endl;
}

// main.cpp
#include "Test.h"
int main()
{
 func(1);
 return 0;
}
// 链接错误:main.obj : error LNK2019: 无法解析的外部符号 "void __cdecl 
func(int)" (?f@@YAXH@Z),该符号在函数 _main 中被引用

八、auto关键字(C++11)  

        1、简述

在早期 C/C++ auto 的含义是:使用 auto 修饰的变量,是具有自动存储器的局部变量
C++11 中,标准委员会赋予了 auto 全新的含义即: auto 不再是一个存储类型指示符,而是作为一 个新的类型指示符来指示编译器, auto 声明的变量必须由编译器在编译时期推导而得
int Test()
{
return 20;
}
int main()
{
int a = 1;
auto b = a;
auto c = 'a';
auto d = Test();
cout << typeid(b).name() << endl;
cout << typeid(c).name() << endl;
cout << typeid(d).name() << endl;
//auto e; 无法通过编译,使用auto定义变量时必须对其进行初始化
return 0;
}

 注意:

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

        2、auto的使用细则 

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

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

       

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

     

        3、auto不能推导的场景 

            3.1 auto不能作为函数的参数
void Test(auto a)
{}
// 此处代码编译失败,auto不能作为形参类型,因为编译器无法对a的实际类型进行推导
            3.2 auto不能直接用来声明数组
int main()
{
	int a[] = { 1,2,3 };
	auto b[] = { 4,5,6 };
}

          3.3 其他情况

 3、为了避免与C++98中的auto发生混淆,C++11只保留了auto作为类型指示符的用法

 4、auto 在实际中最常见的优势用法就是 C++11 提供的新式 for 循环,还有 lambda表达式等进行配合使用。

九、基于范围的for循环(C++11)  

        1、 范围for的语法

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

   对比C++98和C++11对于数组的遍历: 

//C++98中对数组的遍历
void Test()
{
int arr[] = { 1, 2, 3, 4, 5 };
for (int i = 0; i < sizeof(arr) / sizeof(arr[0]); ++i)
     arr[i] *= 2;
for (int* p = arr; p < arr + sizeof(arr)/ sizeof(arr[0]); ++p)
     cout << *p << endl;
}
void Test()
{
int arr[] = { 1, 2, 3, 4, 5 };
for(auto& e : arr)
     e *= 2;
for(auto e : arr)
     cout << e << " ";
return 0;
}

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

        2、范围for的使用条件 

             2.1 for循环迭代的范围必须是确定的

对于数组而言,就是数组中第一个元素和最后一个元素的范围 ;对于类而言,应该提供
begin end 的方法, begin end 就是 for 循环迭代的范围。

举个例子:像一下代码范围就不明确 

void Test(int arr[])
{
    for(auto& e : arr)
        cout<< e <<endl;
}
            2.2 迭代的对象要实现++和==的操作 

十、指针空值nullptr(C++11) 

       C++98中的指针空值 NULL

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

#ifndef NULL
#ifdef __cplusplus
#define NULL   0
#else
#define NULL   ((void *)0)
#endif
#endif
可以看到, NULL 可能被定义为字面常量 0 ,或者被定义为无类型指针 (void*) 的常量 。不论采取何种定义,在使用空值的指针时,都不可避免的会遇到一些麻烦。
void f(int)
{
 cout<<"f(int)"<<endl;
}
void f(int*)
{
 cout<<"f(int*)"<<endl;
}
int main()
{
 f(0);
 f(NULL);
 f((int*)NULL);
 return 0;
}
程序本意是想通过 f(NULL) 调用指针版本的 f(int*) 函数,但是由于 NULL 被定义成 0 ,因此与程序的 初衷相悖。
C++98 中,字面常量 0 既可以是一个整形数字,也可以是无类型的指针 (void*) 常量,但是编译器 默认情况下将其看成是一个整形常量,如果要将其按照指针方式来使用,必须对其进行强转(void *)0。   

 于是在C++11引入了nullptr关键字:

注意:
1. 在使用 nullptr 表示指针空值时,不需要包含头文件,因为 nullptr C++11 作为新关键字引入 的。
2. C++11 中, sizeof(nullptr) sizeof((void*)0) 所占的字节数相同。
3. 为了提高代码的健壮性,在后续表示指针空值时建议最好使用 nullptr
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

爱吃糖的小杨

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值