1. C++的第一个程序
c++中需要把定义文件代码后缀改为.cpp,vs编译器看到.cpp就会调用C++编译器编译。
#define _CRT_SECURE_NO_WARNINGS
#include<iostream>
using namespace std;
int main()
{
cout << "hello world!" << endl;
return 0;
}
2. 命名空间
2.1 命名空间的作用
在C/C++中,变量,函数和类都是大量存在的,这些变量,函数和类的名称将都存在于全局作用域中,可能会导致很多冲突。如下图:
C++引用namespace来更好的解决这类问题,使用命名空间的目的是对标识符的名称进行本地化,以避免命名冲突或名字污染。
2.2 namespace的定义
1. 定义命名空间,需要使用到关键字namespace,后跟命名空间的名字,然后接一对{}即可。{}中即为命名空间的成员,可以是变量,函数或者类型等。
namespace trnjjjjkkhb
{
int rand = 0;
int sum(int x, int y)
{
return x + y;
}
struct Node
{
int data;
struct Node* next;
};
}
2. namespace的本质是定义出一个域,这个域跟全局域各自独立,不同域可以定义同名变量,所以下面的rand不冲突了。
3. C++中域有函数局部域,全局域,命名空间域,类域。域影响的是编译时语法查找一个变量/函数/类型出处(定义和声明)的逻辑。有了域隔离,名字冲突也能解决。局部域和全局域除了会影响编译查找逻辑(就近原则),还会影响变量的生命周期,命名空间域不影响变量的生命周期。
4. namespace只能定义在全局,可以嵌套定义。
#include<stdio.h>
#include<stdlib.h>
namespace trnjjjjkkhb
{
//a
namespace a
{
int rand = 1;
}
//b
namespace b
{
int rand = 2;
}
}
int main()
{
printf("%d\n", trnjjjjkkhb::a::rand);
printf("%d\n", trnjjjjkkhb::b::rand);
return 0;
}
5. 项目工程中多文件定义的同名namespace会认为是同一个namespace,不会冲突。
6. C++标准库都放在一个叫std的命名空间中。
2.3 命名空间的使用
编译查找一个变量的声明/定义时,默认只会在局部或全局查找,不会到命名空间里面去找。当我们要使用命名空间定义的变量/函数时,三种方法:
1. 指定命名空间访问,项目中推荐这种方法。
#include<stdio.h>
namespace trnjjjjkkh
{
int a = 1;
int b = 2;
}
int main()
{
printf("%d\n", trnjjjjkkh::a);
printf("%d\n", trnjjjjkkh::b);
return 0;
}
2. using 将命名空间中的某个成员展开,项目中经常访问的不存在冲突的成员推荐这种方法。
#include<stdio.h>
namespace trnjjjjkkh
{
int a = 1;
int b = 2;
}
using trnjjjjkkh::a;//将命名空间某个成员展开
int main()
{
printf("%d\n",a);
printf("%d\n", a);
printf("%d\n", a);
printf("%d\n", a);
printf("%d\n", a);
printf("%d\n", trnjjjjkkh::a);
printf("%d\n", trnjjjjkkh::b);
return 0;
}
3. 展开命名空间的全部成员,项目不推荐,冲突风险大,日常小练习为了方便使用。
#include<stdio.h>
namespace trnjjjjkkh
{
int a = 1;
int b = 2;
}
using namespace trnjjjjkkh;//展开命名空间全部成员
int main()
{
printf("%d\n", a);
printf("%d\n", b);
return 0;
}
3. C++的输入和输出
1. <iostream> 是Input,Output stream的缩写,是标准的输入输出流,定义了标准的输入输出对象。
2. std::cin 是istream类的对象,它主要面向窄字符(narrow characters (of type char))的标准输入流。
3. std::cout 是ostream类的对象,它主要面向窄字符的标准输出流。
4. std::endl 是一个函数,流插入输出时,相当于插入一个换行字符加刷新缓冲区。
5. <<是流穿插运算符,>>是流提取运算符。
6. 使用C++输入输出更方便,不需要像scanf/printf输入输出时需要手动指定格式,C++输入输出可以自动识别变量类型(本质上是通过函数重载实现的),C++的流可以更好地支持自定义类型的输入输出。
7. cin/cout/endl都属于C++的标准库中,C++标准库放在一个叫std的命名空间中,要通过使用命名空间的方法去使用它们。
#include<iostream>
using namespace std;
int main()
{
int a;
double b;
char c;
cin >> a;
cin >> b >> c;
cout << a << " " << b << " " << c << endl;
return 0;
}
4. 缺省参数
1. 缺省参数(默认参数)是在声明或定义函数时为函数的参数指定一个缺省值。在调用该函数时,如果没有指定实参,则采用该形参的缺省值,否则使用指定的实参,缺省参数分为全缺省和半缺省。
2. 全缺省就是全部形参都给缺省值,半缺省就是部分形参给缺省值。C++规定半缺省参数必须从右往左依次缺省,不能跳跃给缺省值。
3. 带缺省参数的函数调用,C++规定必须从左到右依次给实参,不能跳跃给实参。
4. 函数声明和定义分离时,缺省参数不能在函数声明和定义同时出现,规定必须函数声明时给缺省值。
5. 函数重载
C++支持在同一作用域出现同名函数,但是要求这些同名函数的形参不同,可以是参数个数不同或者类型不同。这样C++函数调用就表现出了多态行为,使用更灵活。
#include<iostream>
using namespace std;
//1.参数类型不同
int sum(int x, int y)
{
cout << "int sum(int x, int y)" << endl;
return x + y;
}
double sum(double x, double y)
{
cout << "double sum(double x, double y)" << endl;
return x + y;
}
//2. 参数个数不同
void fun()
{
cout << "fun()" << endl;
}
void fun(int x)
{
cout << "fun(int x)" << endl;
}
//3. 参数顺序不同,本质上也是参数类型不同
void fun(int x, char y)
{
cout << "fun(int x, int y)" << endl;
}
void fun(char y, int x)
{
cout << "fun(int y, int x)" << endl;
}
注意:
1. C语言不支持同一作用域出现同名函数。
2. 返回值不同不能作为重载条件,因为调用时也无法区分。
6. 引用
6.1 引用的概念和定义
1. 引用是给已存在的变量取了一个别名,编译时不会为引用变量开辟内存空间,它和它引用的变量共用同一片内存空间。
2. 使用:
类型& 引用别名 = 引用对象
6.2 引用的特点
1. 引用在定义时必须初始化。
2. 一个变量可以有多个引用。
3. 引用一旦引用一个实体,就不能引用其他实体。
6.3 引用的使用
1. 引用在实践中主要是用于引用传参和引用做返回值中减少拷贝效率和改变引用对象同时改变被引用对象。
void fun1(int& x, int& y)//引用传参
{
int z = x;
x = y;
y = z;
}
void fun2(int* x, int* y)//指针传参
{
int z = *x;
*x = *y;
*y = z;
}
int main()
{
int a = 10, b = 20;
fun1(a, b);
cout << a << " " << b << endl;
fun2(&a, &b);
cout << a << " " << b << endl;
return 0;
}
2. 引用传参和指针传参的功能时类似的,引用传参更方便一些。
3. 引用和指针在实践中相辅相成,功能有重叠性,但是各有特点,相互不可代替。
6.4 const引用
1. 可以引用一个const对象,但必须用const引用。const亦可以引用普通对象,因为对象的访问权限可以缩小,但不能放大。
2. 临时对象就是编译器需要一个空间暂存表达式的求值结果临时创建的一个未知名的对象,C++把这个未知名的对象叫做临时对象。
3. 要注意的是类似 int& ra = a*3; 这样一些场景下a*3会保存在一个临时对象中。 double d =6.6; int& rd = d;也是一样的,在类型转换过程中会产生临时对象存储中间值,也就是说ra和rd引用的都是临时对象,C++规定临时对象具有常性,所以这里也触发了权限放大,必须用常引用。
6.5 引用和指针的关系
C++中指针和引用两者相辅相成,功能有重叠性,但各有特点,相互不可替代。
1. 语法概念上引用是给一个变量取别名不开空间,指针是存储一个变量的地址要开空间。
2. 引用在定义时必须初始化,指针建议初始化,但在语法上不是必须的。
3. 引用在初始化时引用一个对象,就不能再引用其他的对象;而指针可以不断地改变指向对象。
4. 引用可以直接访问指向对象,而指针要解引用才能访问指向对象。
5. sizeof中含义不同,引用为引用类型的大小,而指针始终时地址空间所占字节数个数(32位平台下占4字节,64位平台下占8个字节)。
6. 指针很容易出现空指针和野指针的问题,引用很少出现,引用使用起来相对安全一些。
7. inline
1. 用inline修饰的函数叫做内联函数,编译时C++编译器会在调用的地方展开内联函数,这样调用内联函数就需要建立栈帧了,就可以提高效率。
2. inline适用于频繁调用且短小的函数,对于递归函数,代码相对于多一些的函数,加上inline也会被编译器忽略。
3. C语言实现宏函数也会在预处理时替换展开,但是宏函数实现很复杂很容易出错,且不方便调试,C++设计了inline目的是替代C语言的宏函数。
4. inline不建议声明和定义分离到两个文件,分离会导致链接错误。因为inline被展开,就没有函数地址,链接时就会报错。
8. nullptr
1. NULL实际是一个宏,在传统的C头文件(stddef.h)中,可以看到如下代码:
2. C++中NULL可能被定义为字面常量0,或者C中被定义为无类型指针(void*)的常量。无论采取何种定义,在使用空值的指针时,都不可避免的遇到一些麻烦,如下:
3. C++11中引入nullptr,nullptr是一个特殊的关键字,nullptr是一种特殊类型的字面量,它可以转换成任意其他类型的指针类型。使用nullptr定义空指针可以避免类型转换问题,引用nullptr只能被隐式地转换为指针类型,而不能转换为整型类型。