引入,第一个c++程序
这是用c++写的helloworld程序
#include<iostream>
using namespace std;
int main()
{cout << "hello,world\n" << endl;return 0;}
接下来我们将根据上述的代码来学习c++的基本语法。
命名空间(namespace)
在c语言中函数被包含在不同的头文件中,但是,这样会出现一个问题,当我们定义的变量与头文件中的变量重名时,在调用时就会出现问题。
#include <stdio.h>
#include <stdlib.h>
int rand = 10;
int main()
{printf("%d\n", rand);return 0;
}
此时编译器就不能区分rand究竟是什么了。
为了解决这一问题,c++引入了命名空间这一概念。c++中将函数,变量封装在命名空间中。
定义命名空间,需要用到namespace关键字,后面跟命名空间的名字,然后接⼀对{}即可,{}中 即为命名空间的成员。命名空间中可以定义变量/函数/类型等。
namespace本质是定义出⼀个域,这个域跟全局域各自独立,不同的域可以定义同名变量,所以不同的命名空间中同名的变量就不在冲突了。
#include <iostream>namespace A {int x = 5;void printX() {std::cout << "x in namespace A: " << x << std::endl;}
}namespace B {int x = 10;
}int main() {A::printX(); // 输出:x in namespace A: 5B::x = 15; // 修改命名空间B中的变量x的值A::printX(); // 输出:x in namespace A: 5,命名空间A中的x并未受到影响return 0;
}
A和B这两个命名空间中的X不会相互影响 。
C++中域有函数局部域,全局域,命名空间域,类域;域影响的是编译时语法查找⼀个变量/函数/ 类型出处(声明或定义)的逻辑,所有有了域隔离,名字冲突就解决了。局部域和全局域除了会影响 编译查找逻辑,还会影响变量的生命周期,命名空间域和类域不影响变量生命周期。
namespace只能定义在全局,当然它还可以嵌套定义。
#include <iostream>
using namespace std;
namespace YG
{int x = 5;namespace XIN {int x = 2;}
}
int main()
{cout << YG::x << endl;cout << YG::XIN::x << endl;return 0;
}
项目工程中多文件中定义的同名namespace会认为是⼀个namespace,不会冲突。
C++标准库都放在⼀个叫std(standard)的命名空间中 。
命名空间的使用
编译查找⼀个变量的声明/定义时,默认只会在局部或者全局查找,不会到命名空间里面去查找。所以 下⾯程序会编译报错。所以我们要使用命名空间中定义的变量/函数,有三种方式:
指定命名空间访问,项目中推荐这种方式。通过作用域解析运算符(::)来精确访问。
#include <iostream>
using namespace std;
namespace YG
{int x = 5;namespace XIN {int x = 2;}
}
int main()
{cout << YG::x << endl;cout << YG::XIN::x << endl;return 0;
}
using将命名空间中某个成员展开,项目中经常访问的不存在冲突的成员推荐这种方式。
//展开命名空间的某个成员
#include <iostream>
using namespace std;
namespace YG
{int x = 5;namespace XIN{int y = 2;}
}
using YG::x;
int main()
{cout << x << endl;cout << YG::XIN::y << endl;return 0;
}
展开命名空间中全部成员,项目不推荐,冲突风险很大,日常小练习程序为了方便推荐使用。
//展开整个命名空间
#include <iostream>
using namespace std;
namespace YG
{int x = 5;namespace XIN{int y = 2;}
}
using namespace YG;
using namespace YG::XIN;
int main()
{cout << x << endl;cout << y << endl;return 0;
}
在这里我们可以发现嵌套定义的命名空间,只展开最高层的命名空间并不能直接访问其包含的其他命名空间中的变量。若要访问其下层的命名空间也需要单独展开。
C++输入&输出
在C++中,iostream 是一个标准库,用于处理输入(input)和输出(output)。它提供了一种方便的方式来进行控制台输入输出操作和文件操作。
具体来说iostream提供了以下主要的类和对象:
• std::cin 是istream类的对象,它主要面向窄字符(narrowcharacters(oftypechar))的标准输入流。
• std::cout 是ostream类的对象,它主要面向窄字符的标准输出流。
• std::endl 是⼀个函数,流插入输出时,相当于插入⼀个换行字符加刷新缓冲区。
• >>是流提取运算符。(C语言还用这两个运算符做位运算左移/右移)
• 使用C++输入输出更方便,不需要像printf/scanf输入输出时那样,需要手动动指定格式,C++的输入输出可以自动识别变量类型(本质是通过函数重载实现),其实最重要的是 C++的流能更好的支持自定义类型对象的输入输出。
#include <iostream>
using namespace std;
int main()
{int a = 0;double b = 0.1;char c = 'x';cout << a << " " << b << " " << c << endl;printf("%d %lf %c\n", a, b,c);// 可以自动识别变量的类型cin >> a;cin >> b >> c;cout << a << endl;cout << b << " " << c << endl;return 0;
}
缺省参数
在 C++ 中,缺省参数(默认参数)允许在函数声明中为一个或多个参数指定默认值。这意味着在调用函数时,如果没有显式提供该参数的值,编译器将使用预先定义的默认值。默认参数的声明使得函数调用更加灵活,并且可以简化函数重载的需求。缺省参数分为全缺省和半缺省参数。
#include <iostream>
#include <assert.h>
using namespace std;
void Func(int a = 0)
{cout << a << endl;
}
int main()
{Func();//没有传参时,使用参数的默认值Func(10); // 传参时,使用指定的实参return 0;
}
全缺省就是全部形参给缺省值,半缺省就是部分形参给缺省值。C++规定半缺省参数必须从右往左 依次连续缺省,不能间隔跳跃给缺省值。
带缺省参数的函数调用,C++规定必须从左到右依次给实参,不能跳跃给实参。
#include <iostream>
using namespace std;
//全缺省void Func1(int a = 10, int b = 20, int c = 30)
{cout << "a = " << a << endl;cout << "b = " << b << endl;cout << "c = " << c << endl << endl;
}
//半缺省void Func2(int a, int b = 10, int c = 20)//半缺省必须从右往左缺省
{cout << "a = " << a << endl;cout << "b = " << b << endl;cout << "c = " << c << endl << endl;
}
int main()
{Func1();Func1(1);Func1(1, 2);Func1(1, 2, 3);Func2(100);Func2(100, 200);//传参必须从左往右依次传。Func2(,200,300)这样是错误的Func2(100, 200, 300);return 0;
}
函数声明和定义分离时,缺省参数不能在函数声明和定义中同时出现,规定必须函数声明给缺省 值。这样可能会出现歧义,如下:
// 声明和定义中分别提供默认参数的情况(非法)
void example(int a, int b = 10); // 声明void example(int a, int b = 20) { // 定义// 函数体
}
函数重载
在 C++ 中,函数重载(Function Overloading)是指允许在同一个作用域内定义多个具有相同函数名但参数列表不同的函数。具体来说,函数重载可以通过以下几种方式实现:
-
参数个数不同: 在同一个作用域内,可以定义参数个数不同的同名函数。例如:
void print(int a); void print(int a, int b);
这里的
print
函数根据参数个数的不同,可以调用不同的实现。 -
参数类型不同: C++ 允许定义参数类型不同的同名函数。例如:
void display(int a); void display(double a);
这里的
display
函数根据参数的类型(整数或浮点数)来决定调用哪一个版本。 -
参数顺序不同: 参数的类型和个数相同,但顺序不同的函数也可以重载。例如:
void drawLine(int x1, int y1, int x2, int y2); void drawLine(int x1, int x2, int y1, int y2);
这里的
drawLine
函数定义了两个版本,分别用于不同顺序的坐标参数。 -
常量性区别: 如果一个函数的参数是常量引用(const 引用),那么可以通过区分参数是否为常量来实现重载。例如:
void process(const int& x); void process(int& x);
这里的
process
函数区分了参数x
是常量引用还是非常量引用。 -
函数重载与返回类型无关: C++ 中,函数的返回类型不能作为重载的标志。也就是说,不能仅仅依靠返回类型的不同来重载函数。
int calculate(int a, int b) {return a + b; }float calculate(int a, int b) {return a * b; // 错误:与前一个函数的返回类型相同,不允许重载 }
这样的两个函数是非法的,不构成函数重载。
引用
在 C++ 中,引用(Reference)是一个允许程序员使用别名访问变量或对象的机制。引用提供了对变量或对象的另一种名称,通过这个名称可以直接访问到原始的变量或对象,而不是其副本。这样可以解决一些指针的问题但不能替代指针。与指针不同的时,指针不指代变量本身,不能直接修改变量。
#include<iostream>
using namespace std;
int main()
{int a = 0;// 引用:b和c是a的别名int& b = a;int& c = a;// 也可以给别名b取别名,d相当于还是a的别名int& d = b;++d;// 这里取地址我们看到是⼀样的cout << &a << endl;cout << &b << endl;cout << &c << endl;cout << &d << endl;return 0;
}
引用只是给变量起了一个别名,所有的别名都只指那一个变量。
引用的特性
引用在定义时必须初始化
在c语言中,指针在定义时,初始化是非强制的,但在c++的引用中,初始化是一种强制的行为。
⼀个变量可以有多个引用
引用⼀旦引用⼀个实体,再不能引用其他实体
#include<iostream>
using namespace std;
int main()
{int a = 10;//int& ra;//编译会报错,引用必须初始化int& b = a;int c = 20;b = c;//这里并非让b引用c,因为C++引用不能改变指向,//这里是赋值,a,b的值都变为20cout << &a << endl;cout << &b << endl;cout << &c << endl;return 0;
}
引用的使用
引用在实践中主要是于引⽤传参和引⽤做返回值中减少拷贝提高效率和改变引用对象时同时改变被 引用对象。
引用传参跟指针传参功能是类似的,引用传参相对更方便⼀些
void Swap(int& rx, int& ry){int tmp = rx;rx = ry;ry = tmp;}int main(){int x = 0, y = 1;cout << x <<" " << y << endl;Swap(x, y);cout << x << " " << y << endl;return 0;}
const引用
在 C++ 中,const引用是一种特殊类型的引用,其声明中包含了const关键字,const引用允许我们通过引用访问对象,同时确保在引用生命周期内不会修改所引用的对象(只读不写)。
int main()
{const int a = 10;// 编译报错:error C2440 : “初始化” :⽆法从“const int”转换为“int& ”//这里的引用是对a访问权限的放大//int& ra = a;//这样才可以const int& ra = a;// 编译报错:error C3892 : “ra”:不能给常量赋值//ra++;//这里的引用是对b访问权限的缩小//rb++;int b = 20;const int& rb = b;// 编译报错:error C3892 : “rb”:不能给常量赋值return 0;
}