【C++】C++入门——(上)
前言:结束了上一阶段的数据结构的学习,就逐步的步入到C++的学习了。C++是一个更高级的语言,这也意味着它相比C语言有更多的优势也会更加便捷;但是难度相较于C语言也比较大。小编希望与大家共同学习将c++学到一个不错的水平,加油!
文章目录
- 一,C++介绍
- 二,C++语法
- 2.1命名空间
- 2.2namespace的定义
- 2.3命名空间的使用
- 三,C++输入和输出
- 四,缺省参数
一,C++介绍
C++ 是一种通用编程语言,由 Bjarne Stroustrup 于 1985 年在贝尔实验室开发。它是对 C 语言的扩展,引入了面向对象编程(OOP)特性,同时保留了对底层硬件的直接操作能力。
C++的核心特性:
- 面向对象:支持类、继承、多态和封装。
- 高性能:允许直接内存操作,适合系统级开发。
- 泛型编程:通过模板实现代码复用。
- 标准库:提供丰富的容器、算法和输入输出功能。
以上就是C++的介绍,要想学好一门语言就要先从其语法开始学习,下面就来介绍一下C++的语法。
二,C++语法
回忆一下在学C语言的时候第一个程序是怎样写的?
#include<stdio.h>
int main()
{printf("hello world!\n");return 0;
}
相信大部分人在初学C语言的时候就写过这样的代码。那么这段程序如果换成C++应该怎么写呢?
这里有人会有疑问说C++是高级语言可以兼容C这个写法在C++中也能编译通过。
这样的做法的确可以,但是C++拥有一套自己的输入输出,下面我们来看代码:
#include<iostream>
using namespace std;
int main()
{cout<<"hello world!\n"<<endl;return 0;
}
以上就是C++写hello world的写法,一眼望去C++与C的代码行数有些不同,C++比C多了一行using namespace std;
。到这可能会有人问,这一行代码代表什么?有什么含义?这就是小编接下来要讲的语法点命名空间。
2.1命名空间
什么是命名空间?命名空间用来解决什么问题?下面我们用一个例子来引入
#include <stdio.h>
#include <stdlib.h>int rand = 10;
int main()
{// 编译报错:error C2365: “rand”: 重定义;以前的定义是“函数”printf("%d\n", rand);return 0;
}
上面这个例子我们看到编译器报了rand重定义的错误,要了解为什么会报重定义我们还需要看下面这个例子:
#include<stdio.h>
int x = 10;
int main()
{int x = 20;printf("%d", x);return 0;
}
先来看看上面这段程序输出是什么?当然有了前面C语言的基础这里一看就是输出的是20。那为什么我们能判断是20而不是10呢?这其实就是这两个x的作用域不同,一个在全局域(静态区),一个在局部域(函数栈帧)这就足以将它们区分开。
如果将外面的x拿进去,会发生什么?
#include<stdio.h>
int main()
{int x = 10;int x = 20;printf("%d", x);return 0;
}
这时候编译运行就会发现报了一个重定义的错误:
这次的错误就与上面我们引出的例子报的错误相似了,我们不妨再变一下:
#include<stdio.h>
int add(int x,int y)
{return x+y;
}
int add=10;
int main()
{int x = 20;printf("%d", x);return 0;
}
看到这是不是就和上面那个例子报的错误一样了,那我们只要分析这段代码的问题就可以知道上面代码的问题了。我们先来看错误说重定义,以前定义的是函数,这个很好理解说白了就是它们的名称冲突了,都在全局域名称又一样所以编译器碰到add时分不清是哪个,上面rand重定义也是一个道理,它是与头文件中定义的rand冲突了。
那么如果想要定义同名变量或函数怎么办呢?
答案就是使用命名空间将他们隔离。所以我们看到命名空间解决的就是在同一个域的命名冲突的问题。
2.2namespace的定义
- 定义命名空间,需要使⽤到namespace关键字,后⾯跟命名空间的名字,然后接⼀对{}即可,{}中即为命名空间的成员。命名空间中可以定义变量/函数/类型等。
还是上面的例子如果想让它们不冲突,就可以使用命名空间:
#include <stdio.h>
#include <stdlib.h>
//命名空间里可以定义 变量 函数 结构体
namespace slu
{int rand = 10;int Add(int x, int y){return x + y;}struct Node{struct Node* next;int val;};
}
int Add=10;
int x=10;
int main()
{int x = 1;//printf("%d\n", y);//不指定域 在寻找x的时候就先局部后全局printf("%d\n", x);//如果想访问外部的这个x就加上 :: 域作用限定符指定全局域printf("%d\n",::x);printf("%p\n", rand);//指定访问slu这个命名空间里的randprintf("%d\n", slu::rand);return 0;
}
-
从上面的代码可以看到namespace本质是定义出⼀个域,这个域跟全局域和局部域各自独立,不同的域可以定义同名变量,所以下面的rand不在冲突了。
-
注意C++中的域有局部域,全局域,类域,命名空间域,域主要影响的就是编译器在编译时查找语法出处的一个逻辑。所以同名变量在不同的区域,同名的冲突就得到了解决,就像一个班级里面如果有同名的学生老师也会分不清哪个是哪个,如果是不同班级的同名学生由于班级不同就能够区别开来。
-
局部域和全局域除了会影响编译查找逻辑,还会影响变量的生命周期,命名空间域和类域不影响变量sheng命周期。
-
两个同名的命名空间可以合并看作是一个,不会冲突。
-
命名空间还支持嵌套定义比如:
namespace stack
{namespace A{void Add(int x,inty){return x+y;}}namespace B{void Add(int* x,int* y){int tmp=*x;*x=*y;*x=tmp;}}
}
假如有个命名空间的名称为stack 有一个程序A对stack这个命名空间里的内容进行修改,同时又有一个程序员B也修改的是同一个地方 那么为了区分它们的内容就用命名空间分开。由于它们修改的是stack所以嵌套在stack这个命名空间里面。
2.3命名空间的使用
命名空间的三种使用方法:
- 指定命名空间访问
namespace A
{int a=10;int b=20;
}
int main()
{//printf("%d",b);会编译报错,局部域全局域都找不到Aprintf("%d",A::b);return 0;
}
- 将命名空间中的某个成员展开
namespace A
{int a = 10;int b = 20;
}using A::b;
int main()
{printf("%d\n", A::b);//展开了b就相当于把b放在了全局域上printf("%d\n", b);return 0;
}
- 将命名空间全部展开,冲突风险很大。
namespace A
{int a = 10;int b = 20;
}using namespace A;
int main()
{//全部展开 那么命名空间里的成员就都不用指定了。printf("%d\n", A::b);printf("%d\n", b);printf("%d\n",A:a);printf("%d\n",a);return 0;
}
三,C++输入和输出
还是上面那段代码:
#include<iostream>
using namespace std;
int main()
{cout<<"hello world!\n"<<endl;return 0;
}
我们已经解释了命名空间了,接下来就剩头文件和main函数里的代码了。
- 首先看头文件
<iostream>
这时Input Output Stream 的缩写,它是标准的输入输出流库,定义了标准的输入输出流对象。 std::cou
t是ostream
类的对象它主要⾯向窄字符的标准输出流。
std::cin
是istream
类的对象它主要面向窄字符的标准输入输出流。
<<
与>>
在C语言中是移位运算符,但在C++中<<
是流插入,>>
是流提取。endl
是endline的简写它是一个函数,流插入输出时,相当于插入一个换行字符加刷新缓冲区。- cout/cin/endl都包含在C++标准库中,而C++标准库又包含在一个叫std(standard)的命名空间中,因此要使用就要展开或指定展开命名空间。
- C++这套输入输出相比C语言优势在于不用管类型了;printf 指定格式 语法原生定义类型即内置类型像(int double char),不支持类类型,而cout与cin它会自动识别类型,而且也支持自定义类型,可以说它的本质就是为了支持任意类型输出输入。关于类的问题将会在类和对象章节介绍。
#include<iostream>
using namespace std;
int main()
{int a=10;double b=20;char c="x";std::cout<<a<<" "<<b<<" "<<c<<" "<<std::endl;//展开了命名空间就不用指定命名空间了cout<<a<<" "<<b<<" "<<c<<" "<<endl;scanf("%d %lf", &a, &b);printf("%d %lf\n", a, b);cin >> a;cin >> b >> c;cout << a << endl;cout << b << " " << c << endl;return 0;
}
四,缺省参数
- 缺省参数就是在函数声明或定义的时候为函数的参数指定的一个缺省值,在调用函数时如果没有传对应参数的实参,那么就默认使用缺省值;如果传了对应的实参那么就使用指定传过来的那个参数。
#include<iostream>
using namespace std;
void func(int x=10)
{cout<<x<<endl;
}
int main()
{func();//没有指定传的实参 就使用缺省值此时x为10func(1);//指定传了实参 就使用传过去的实参 此时x为1return 0;
}
- 缺省值还分全缺省和半缺省,这应用在函数有多个参数的场景。缺省值就是函数的参数都给了特定的值(缺省值),半缺省就是部分函数的参数给了特定的值。
#include<iostream>
using namespace std;
//全缺省
void func1(int a=1,int b=2,int c=3)
{cout<<"a="<<a<<" "<<endl;cout<<"b="<<b<<" "<<endl;cout<<"c="<<c<<" "<<endl;
}
//半缺省
void func2(int a,int b,int c=3)
{cout<<"a="<<a<<" "<<endl;cout<<"b="<<b<<" "<<endl;cout<<"c="<<c<<" "<<endl;
}
int main()
{func1();//abc都不传有缺省值1,2,3func1(10,20);//只传ab c有缺省值10 20 3func1(10,20,30);//都传abc 10 20 30//c传实参有缺省值3func2(10,20);//c传实参有缺省值100func2(10,20,100);}
注意半缺省在给缺省值的时候要从右到左依次给缺省值,在调用函数传值的时候要从左往右依次传值,不能跨越传值,也不能跨越的给缺省值!
- 当函数的声明和定义分离时,缺省参数不能在声明和定义中同时出现,规定必须在函数的声明那里给缺省值,也就是只能给在头文件中的函数函数声明加缺省值。
举个例子:
在头文件中定义一个栈:
typedef int STDataType;
typedef struct Stack
{
STDataType* a;
int top;
int capacity;
}ST;
//在这里给上缺省值
void STInit(ST* ps, int n = 4);
在.cpp文件中
void STInit(ST* ps, int n)
{
assert(ps && n > 0);
ps->a = (STDataType*)malloc(n * sizeof(STDataType));
ps->top = 0;
ps->capacity = n;
}
这里其实也体现了,缺省参数的应用,当我们不知道要开多大的空间那么在使用
STInit
这个函数的时候就不传第二个参数,让他默认使用缺省值,当我们指定传值过去的时候它就会根据我们的值开空间,这样使用起来非常的方便。
以上就是本章的全部内容啦!
最后感谢能够看到这里的读者,如果我的文章能够帮到你那我甚是荣幸,文章有任何问题都欢迎指出!制作不易还望给一个免费的三连,你们的支持就是我最大的动力!