C++入门

目录

一、C++的输入和输出

二、缺省参数/默认参数

2.1 缺省参数的概念

2.2 缺省参数的定义及应用

a.全缺省

b.半缺省

三、函数重载

函数重载的概念

四、引用

4.1 引用的概念

4.2 引用类型的定义

4.3 引用特性

4.4 引用和指针的关系

4.5 引用的使用场景

4.5.1 做参数

4.5.2 做返回值

4.6 引用和指针的区别

五、内联函数

5.1 概念

5.2 特性

六、关键字 auto

6.1 auto简介

6.2 auto的使用细则

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

6.2.2 在同一行定义多个变量

6.3 auto不能推导的场景

6.3.1 auto不能作为函数的参数

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

6.3.3 

6.4.4

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

7.1 范围for的语法

7.2 范围for的使用条件

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

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

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

8.1 C++98中的指针空值


一、C++的输入和输出

现在尝试用C++写出第一个程序:

#include<iostream>

using namespace std;

int main()
{
    cout << "hello world" << endl;
    
    return 0;
}

说明:

1. 与C程序一样,首先我们需要包头文件:iostream --> 是输入输出流

2.  <<  

  • 左移操作符
  • 流插入     ——     自动识别类型(不需要手动控制格式)

 3. cout

  • c: console   -->  控制台
  • out:输出
  • endl:endline  --> '\n'

 上面这个是输出操作,下面再来个输入:

#include<iostream>
using namespace std;

int main()
{

    int i;
    char ch;

    cin >> i >> ch;

    cout << d << endl;

    return 0;
}

 说明:

1.  >> 

  • 右移操作符
  • 流提取

2.cin

  • c:console   -->  控制台
  • in:  输入

小扩展:

C++中 cout 如何控制小数精度

#include<iostream>
using namespace std;

int main()
{

    double d = 1.11111;
    
    cout.precision(2);    // 圆括号里的数字即代表精确位数
    cout << d << endl;
    
    return 0;
}

        这里暂时只做了解,因为涉及后面对象的知识。当然,C++兼容C,我们也可以用我们熟悉的printf函数来解决精度问题。

std 命名空间的使用惯例:
std C++ 标准库的命名空间,如何展开 std 使用更合理呢?
1. 在日常练习中,建议直接 using namespace std 即可,这样就很方便。
2. using namespace std 展开,标准库就全部暴露出来了,如果我们定义跟库重名的类型 /对 象/ 函数,就存在冲突问题。该问题在日常练习中很少出现,但是项目开发中代码较多、规模大,就很容易出现。所以建议在项目开发中使用,像std::cout 这样使用时指定命名空 +using std::cout展开常用的库对象 / 类型等方式。

二、缺省参数/默认参数

2.1 缺省参数的概念

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

 缺省参数分为:全缺省 和 半缺省

注意:这里的 “半” 不是缺省一半的参数的意思,而是有多个参数缺省,但不是全部。

2.2 缺省参数的定义及应用

a.全缺省

#include<iostream>
using namespace std;

//全缺省
void Func(int a = 0)
{
	cout << a << endl;
}

void Func2(int a = 10, int b = 20, int c = 30)
{
	cout << "a = " << a << endl;
	cout << "a = " << b << endl;
	cout << "a = " << c << endl;
}

int main()
{
	Func(1);	//有参
	Func();		//无参

	Func2(1, 2, 3);
	Func2(1, 2);	// 不传全,默认从左向右依次覆盖
	return 0;
}

b.半缺省

#include<iostream>
using namespace std;

// 半缺省    只能从右往左连续给
void Func2(int a, int b = 20, int c = 30)
{
	cout << "a = " << a << endl;
	cout << "a = " << b << endl;
	cout << "a = " << c << endl;
}

int main()
{
	Func2(1, 2, 3);
	Func2(1, 2);	// 不传全,默认从左向右依次覆盖
	return 0;
}

注意:

1. 半缺省参数必须 从右往左依次 来给出,不能间隔着给
2. 缺省参数不能在函数声明和定义中同时出现
  //a.h
  void Func ( int a = 10 );
  // a.cpp
  void Func ( int a = 20 )
{}
  // 注意:如果生命与定义位置同时出现,恰巧两个位置提供的值不同,那编译器就无法确定到底该用那个缺省值。
3. 缺省值必须是常量或者全局变量
4. C 语言不支持(编译器不支持)

三、函数重载

函数重载的概念

函数重载: 是函数的一种特殊情况, C++ 允许在 同一作用域中 声明几个功能类似 的同名函数 ,这些同名函数的形参列表 ( 参数个数 或 类型 或 类型顺序 ) 不同 ,常用来处理实现功能类似数据类型不同的问题。
/*
	C语言不支持重载		链接时,直接用函数名去找地址,有同名函数,区分不开
	Cpp如何支持的呢?	函数名修饰规则,函数名字中引入参数类型,各个编译器自己实现一套
*/

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

int main()
{
	Add(1, 2);
	Add(1.1, 2.2);

	f(1, 'a');
	f('a', 1);

	return 0;
}

四、引用

4.1 引用的概念

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

4.2 引用类型的定义

类型 & 引用变量名 ( 对象名 ) = 引用实体;
void TestRef()
{
    int a = 10;
    int& ra = a;//<====定义引用类型
    printf("%p\n", &a);
    printf("%p\n", &ra);
}
注意: 引用类型 必须和引用 实体 同种类型

4.3 引用特性

1. 引用在 定义时必须初始化
2. 一个变量可以有多个引用
3. 引用一旦引用一个实体,再不能引用其他实体
void Test()
{
	int a = 0;

	//1.引用必须初始化
	//int& b;	这样写会报错,错误语法

	//2.引用定义后,不能改变指向
	int& b = a;
	int c = 2;
	//b = c;	这样写就变成了把c的值赋给b(a)了

	//3.一个变量可以有多个引用(多个别名)
	int& d = a;

	return 0;
}

4.4 引用和指针的关系

  • 指针和引用的功能是类似的、重叠的。
  • C++的引用,是对指针使用比较复杂的场景进行一些替换,让代码更简单移动,但是不能完全替代指针。
  • 引用不能完全替代指针的原因:引用定义后,不能改变指向。
  • 比如在做数据结构,链表的操作等,引用就不适用了,因为需要用指针改变指向。

4.5 引用的使用场景

4.5.1 做参数

//1.做参数(a、输出型参数, b、对象较大时,减少拷贝,提高效率)
//  这些效果,指针也可以,但是引用更方便
void Swap(int& a, int& b)
{
	int temp = a;
	a = b;
	b = temp;
}

int mian()
{
	int x = 0, y = 1;
	
	Swap(x, y);

	return 0;
}

4.5.2 做返回值

int& Count()
{
   static int n = 0;
   n++;
   // ...
   return n;
}

注意:

如果函数返回时,出了函数作用域,如果返回对象还在 ( 还没还给系统 ) ,则可以使用 引用返回,如果已经还给系统了,则必须使用传值返回。

4.6 引用和指针的区别

语法上:

  1. 引用是别名,不开空间;指针是地址,需要开空间存地址
  2. 引用必须初始化,指针可以初始化也可以不初始化
  3. 引用不能改变指向
  4. 引用相对更安全,没有空引用,但是有空指针,容易出现野指针,但是不容易出现野引用
  5. sizeof、++、解引用访问登方面的区别

用法和底层:

  •     汇编层面上,没有引用,斗是指针,引用编译后也转换成指针了

五、内联函数

如需频繁调用一个函数,则需要频繁建立栈帧,在C语言中,可以用宏函数的方式来解决这个问题。

 首先来复习一下宏函数:

// 容易犯错注意点:
// 1. 宏函数不是函数
// 2. 宏定义不要带 ';'
// 3. 括号控制优先级
// 核心店:宏是预处理阶段进行替换
#define ADD(a, b) ((a)+(b))

// 为什么要给 a b 分别加括号?
// 因为传过来的 a b 可能是一个表达式

 宏的缺点:

  1. 语法复杂,坑很多,不容易控制
  2. 不能调试
  3. 没有类型安全的检查

于是C++就对这个问题进行了优化,我们看看C++如何解决的。

5.1 概念

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

如果在上述函数前增加 inline 关键字将其改成内联函数,在编译期间编译器会用函数体替换函数的
调用。
查看方式:
1. release 模式下,查看编译器生成的汇编代码中是否存在 call Add
2. debug 模式下,需要对编译器进行设置,否则不会展开 ( 因为 debug 模式下,编译器默认不
会对代码进行优化,以下给出 vs2013 的设置方式 )

5.2 特性

1. inline 是一种 以空间换时间 的做法,如果编译器将函数当成内联函数处理,在 编译阶段,会
用函数体替换函数调用 ,缺陷:可能会使目标文件变大,优势:少了调用开销,提高程序运
行效率。
2. inline 对于编译器而言只是一个建议,不同编译器关于 inline 实现机制可能不同 ,一般建
议:将 函数规模较小 ( 即函数不是很长,具体没有准确的说法,取决于编译器内部实现 )
是递归、且频繁调用 的函数采用 inline 修饰,否则编译器会忽略 inline 特性。下图为
C++prime 》第五版关于 inline 的建议:
3. inline 不建议声明和定义分离,分离会导致链接错误。因为 inline 被展开,就没有函数地址
了,链接就会找不到。
// F.h
#include <iostream>
using namespace std;
inline void f(int i);
// F.cpp
#include "F.h"
void f(int i)
{
 cout << i << endl;
}
// main.cpp
#include "F.h"
int main()
{
 f(10);
 return 0;
}
// 链接错误:main.obj : error LNK2019: 无法解析的外部符号 "void __cdecl
// f(int)" (?f@@YAXH@Z),该符号在函数 _main 中被引用

六、关键字 auto

6.1 auto简介

在早期 C/C++ auto 的含义是:使用 auto 修饰的变量,是具有自动存储器的局部变量 ,但遗憾的
是一直没有人去使用它,大家可思考下为什么?
C++11 中,标准委员会赋予了 auto 全新的含义即: auto 不再是一个存储类型指示符,而是作为一
个新的类型指示符来指示编译器, auto 声明的变量必须由编译器在编译时期推导而得

【注意】

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

6.2 auto的使用细则

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

auto 声明指针类型时,用 auto auto* 没有任何区别,但用 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;
    *a = 20;
    *b = 30;
     c = 40;
    return 0;
}

6.2.2 在同一行定义多个变量

当在同一行声明多个变量时,这些变量必须是相同的类型,否则编译器将会报错,因为编译
器实际只对第一个类型进行推导,然后用推导出来的类型定义其他变量
void TestAuto()
{
    auto a = 1, b = 2; 
    auto c = 3, d = 4.0;  // 该行代码会编译失败,因为c和d的初始化表达式类型不同
}

6.3 auto不能推导的场景

6.3.1 auto不能作为函数的参数

// 此处代码编译失败,auto不能作为形参类型,因为编译器无法对a的实际类型进行推导
void TestAuto(auto a)
{}

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

void TestAuto()
{
    int a[] = {1,2,3};
    auto b[] = {4,5,6};
}

6.3.3 

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

6.4.4

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

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

7.1 范围for的语法

对于一个 有范围的集合 而言,由程序员来说明循环的范围是多余的,有时候还会容易犯错误。因
C++11 中引入了基于范围的 for 循环。 for 循环后的括号由冒号 分为两部分:第一部分是范
围内用于迭代的变量,第二部分则表示被迭代的范围
void TestFor()
{
    int array[] = { 1, 2, 3, 4, 5 };
    for(auto& e : array)
         e *= 2;
    for(auto e : array)
         cout << e << " ";
    return 0;
}
注意:与普通循环类似,可以用 continue 来结束本次循环,也可以用 break 来跳出整个循环

7.2 范围for的使用条件

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

对于数组而言,就是数组中第一个元素和最后一个元素的范围 ;对于类而言,应该提供
begin和 end 的方法, begin end 就是 for 循环迭代的范围。
注意:以下代码就有问题,因为 for 的范围不确定
void TestFor(int array[])
{
    for(auto& e : array)
        cout<< e <<endl;
}

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

迭代器的问题,会在以后得章节进行详细讲解。

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

8.1 C++98中的指针空值

在良好的 C/C++ 编程习惯中,声明一个变量时最好给该变量一个合适的初始值,否则可能会出现不可预料的错误,比如未初始化的指针。如果一个指针没有合法的指向,我们基本都是按照如下方式对其进行初始化:
void TestPtr()
{
    int* p1 = NULL;
    int* p2 = 0;
    // ……
}
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。
注意:
1. 在使用 nullptr 表示指针空值时,不需要包含头文件,因为 nullptr C++11 作为新关键字引入
2. C++11 中, sizeof(nullptr) sizeof((void*)0) 所占的字节数相同。
3. 为了提高代码的健壮性,在后续表示指针空值时建议最好使用 nullptr。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值