前言:
从这篇文章开始,将进入C++阶段的学习,此篇文章是c++的第一站的上半篇,讲述C++初阶的知识
目录
什么是C++
C++的发展史
C++关键字(C++98)
命名空间
命名空间定义
命名空间使用
1.加命名空间名称及作用域限定符
2.使用using将命名空间中某个成员引入
3.使用using namespace 命名空间名称引入
C++输入&输出
缺省参数
缺省参数概念
全缺省参数
半缺省参数
半缺省参数的应用
函数重载
函数重载概念
1、参数类型不同(函数名相同)
2、参数个数不同
3.参数类型顺序不同
不构成函数重载:
1.返回值不同不能构成函数重载
2.不同的命名空间域内
3.函数缺省的函数重载
什么是C++
C语言是结构化和模块化的语言,适合处理较小规模的程序。对于复杂的问题,规模较大的程序,需要高度的抽象和建模时,C语言则不合适。为了解决软件危机, 20世纪80年代, 计算机界提出了OOP(object oriented programming:面向对象)思想,支持 面向对象 的程序设计语言应运而生。1982年,Bjarne Stroustrup博士在C语言的基础上引入并扩充了面向对象的概念,发明了一种新的程序语言。为了表达该语言与C语言的渊源关系,命名为C++。因此:C++是基于C语言而产生的,它既可以进行C语言的过程化程序设计,又可以进行以抽象数据类型为特点的基于对象的 程序设计,还可以进行面向对象的程序设计。
C++的发展史
C++关键字(C++98)
C语言的关键字:
命名空间
#include<stdio.h>
#include<stdlib.h>
//命名冲突
int rand = 10;
// C语言没办法解决类似这样的命名冲突问题,所以C++提出了namespace来解决
int main()
{
printf("%d\n",rand);
// 编译后后报错:error C2365: “rand”: 重定义;以前的定义是“函数”
return 0;
}
执行:
解释:
在C语言中,
rand
是一个库函数,用于生成伪随机数。然而,在该代码中,rand
被重新定义为一个整数变量并赋值为10。这导致命名冲突。
要解决这个问题,你可以考虑以下两种方法之一:
- 改变变量名:将变量名
rand
更改为其他名称,以避免与库函数rand
发生冲突。- 使用C++:将代码保存为以
.cpp
为扩展名的文件,并使用命名空间来解决命名冲突。
命名空间定义
// 1. 正常的命名空间定义
namespace bit
{
// 命名空间中可以定义变量/函数/类型
int rand = 10;
int Add(int left, int right)
{
return left + right;
}
struct Node
{
struct Node* next;
int val;
};
}
1.2命名空间可以嵌套
namespace zzc
{
//命令空间中可以定义变量/函数/类型
int rand = 10;
int Add(int left, int right)
{
return left + right;
}
struct Node
{
struct Node* next;
int val;
};
namespace zzc1
{
int rand = 1;
}
}
int Add(int left, int right)
{
return (left + right) * 10;
}
int main()
{
printf("%d\n",zzc::rand);
printf("%d\n", zzc::zzc1::rand);//命名空间的嵌套
printf("%d\n",Add(1,2));
printf("%d\n",zzc::Add(1,2));
struct zzc::Node node;//命名空间里面的结构体类型创建变量
return 0;
}
代码执行:
注意:一个命名空间就定义了一个新的作用域 ,命名空间中的所有内容都局限于该命名空间中
#include<stdio.h>
#include<iostream>
#include<stdlib.h>
//Stack.h
namespace zzc
{
void StackInit()
{
cout << "void StackInit()" << endl;
}
namespace zzc1
{
int rand = 0;
}
}
//Queue.h
namespace zzc
{
void QueueInit()
{
cout << "void QueueInit()" << endl;
}
namespace zzc
{
int rand = 1;
}
}
using std::cout;
using std::endl;
#include"Stack.h"
#include"Queue.h"
int main()
{
//命名空间的合并
zzc::StackInit();
zzc::QueueInit();
return 0;
}
执行:
命名空间使用
namespace bit
{
// 命名空间中可以定义变量/函数/类型
int a = 0;
int b = 1;
int Add(int left, int right)
{
return left + right;
}
struct Node
{
struct Node* next;
int val;
};
}
int main()
{
// 编译报错:error C2065: “a”: 未声明的标识符
printf("%d\n", a);
return 0;
}
执行:
1.加命名空间名称及作用域限定符
在C++中,::
被称为作用域限定符(Scope Qualifier)。作用域限定符用于指定特定的作用域,并在该作用域中查找标识符。
通过使用作用域限定符,你可以在命名空间、类、结构体、枚举等作用域中精确地引用特定的成员或标识符。
作用域限定符的语法是namespace后面的(zzc)::identifier
或 class::identifier
,其中namespace
是命名空间的名称(zzc),class
是类的名称,identifier
是成员或标识符的名称。
namespace zzc
{
//命令空间中可以定义变量/函数/类型
int rand = 10;
}
执行:
2.使用using将命名空间中某个成员引入
namespace zzc
{
//命令空间中可以定义变量/函数/类型
int rand = 10;
int Add(int left, int right)
{
return left + right;
}
struct Node
{
struct Node* next;
int val;
};
//嵌套
namespace zzc1
{
int rand = 1;
}
}
//部分展开(授权)
using zzc::Add;
int main()
{
printf("%d\n",zzc::Add(1,2));
printf("%d\n",Add(1,2));
}
3.使用using namespace 命名空间名称引入
全部展开(授权)--展开命名空间,意思是把所有墙都给它拆掉,把命名空间里面所有内容展开,默认是找全局的,那就没有区别之分了,所以很容易引起命名冲突,是一件很危险的事情
在使用
using namespace
时,存在一些潜在的风险和注意事项,因此需要谨慎使用。以下是一些相关的考虑因素:
命名冲突:如果你使用
using namespace
引入了多个命名空间,而这些命名空间中有相同名称的成员,就会导致命名冲突。这可能会使代码难以理解,且不明确引用的成员可能会导致错误的调用。命名空间污染:使用
using namespace
会将整个命名空间的所有成员引入当前作用域,这可能导致命名空间的成员与当前作用域中的其他标识符发生冲突。这可能会引入不必要的歧义和错误。可读性和维护性:在代码中广泛使用
using namespace
可能会降低代码的可读性和维护性。其他开发人员可能不熟悉引入的命名空间,并且很难追踪代码中使用的具体成员的来源。
在展开namespace之前运行:
由于rand函数是头文件stdlib.h里面的内容,所以不初始化的时候是随机值。(rand函数的函数名就是函数的地址)
在展开zzc这个namespace之后:
#include<stdlib.h>
using namespace zzc;
可以发现,rand的打印不明确,这是为什么呢?原因是,命名空间zzc相当于用一道墙把命名空间域里面的内容和全局变量隔离起来了,现在使用using namespace关键字,相当于把墙拆了,就引起了名字冲突 -- zzc里面rand函数与头文件<stdlib.h>里面的rand函数的冲突。
总结:还是指定最安全,不要把"墙"拆掉(但是指定需要大量用到,如果觉得命名空间zzc::使用次数过多,那么请使用上面第2点的部分展开)
C++输入&输出
C语言的输入、输出函数分别是scanf、printf等,那么C++是怎么输入输出的呢?
首先来讲一下头文件的展开:
#include<iostream> // 记忆方式:input + output + 流
头文件的展开意思是,把以下在iostream库里面的代码都复制到Test.cpp这个源文件下。
作用:
在使用cout和cin之前,通常需要引用头文件<iostream>来包含所需的定义和声明。这是因为cout和cin是iostream库中的对象,而<iostream>头文件包含了这些对象的定义。
如果在代码中没有引用<iostream>头文件,编译器可能无法找到cout和cin的定义,从而导致编译错误。因此,为了正确地使用cout和cin,通常需要在代码中引用<iostream>头文件。
接着来讲一下std的展开:
using namespace std;//展开是为了给使用者授权,也就是说把 '墙拆开'
是一个C++中的指令,它的作用是将标准命名空间
std
中的所有名称引入当前的命名空间,以便在代码中直接使用这些名称,而无需在前面加上std::
的前缀。C++的标准库(包括iostream、string、vector等)中的定义都位于
std
命名空间中。当我们使用标准库中的函数、类或对象时,通常需要在前面加上std::
前缀来指明它们来自于std
命名空间使用
std::cout
和std::cin
来访问标准输出和标准输入对象然而,为了简化代码并提高可读性,可以使用
using namespace std
指令将std
命名空间中的名称引入当前命名空间。这样,在代码中就可以直接使用cout
和cin
,而无需每次都加上std::
前缀。但也存在一些潜在的问题。由于引入了整个
std
命名空间,可能会导致命名冲突问题,特别是在大型项目中或与其他库/代码一起使用时。因此,在头文件中通常不建议使用using namespace std;
以避免可能的命名冲突。在源文件中使用时,可以根据个人偏好和代码的规模来决定是否使用该指令
说明:
1.使用cout标准输出对象(控制台)和cin标准输入对象(键盘)时,必须包含< iostream >头文件以及按命名空间使用方法使用std。2.cout和cin是全局的流对象,endl是特殊的C++符号,表示换行输出,他们都包含在包含<iostream >头文件中。3.<<是流插入运算符,>>是流提取运算符。4.使用C++输入输出更方便,不需要像printf/scanf输入输出时那样,需要手动控制格式。C++的输入输出可以自动识别变量类型。5.实际上cout和cin分别是ostream和istream类型的对象,>>和<<也涉及运算符重载等知识,这些知识我们我们后续才会学习,所以我们这里只是简单学习他们的使用。后面我们还有有一个章节更深入的学习IO流用法及原理
代码实现:
#include <iostream>
using namespace std;
int main()
{
int a;
double b;
char c;
int i;
int j;
// 可以自动识别变量的类型
cout << "输入:";
cin >> a;
cin >> b >> c;
cout << "输出:"<<endl;
cout << a << endl;
cout << b << " " << c << endl ;
cout << "打印地址:" << endl;
cout << &i << endl;
cout << &j << endl;
return 0;
}
缺省参数
缺省参数概念
#include<iostream>
using namespace std;
void Func(int a =1)
{
cout << a << endl;
}
int main()
{
Func(2);// 传参时,使用指定的实参
Func();// 没有传参时,使用参数的默认值(a = 1)
return 0;
}
执行:
4.2 缺省参数分类
全缺省参数
显示传参,传参从左往右传
void Func(int a = 10 , int b = 10,int c=10)
{
cout << "a=" << a <<endl;
cout << "b=" << b <<endl;
cout << "c=" << c <<endl <<endl ;
}
int main()
{
//显示传参,从左往右显示传参
Func(1);
Func(1,2);
Func(1, 2, 3);
return 0;
}
代码执行:
半缺省参数
1. 半缺省参数必须从右往左依次来给出,不能间隔着给
// 半缺省 -- 必须从右往左,给缺省值
#include<iostream>
using namespace std;
void Func(int a,int b=10,int c=20)
{
cout << "a=" << a << endl;
cout << "b=" << b << endl;
cout << "c=" << c << endl<<endl;
}
int main()
{
Func(1);
Func(1, 2);
Func(100, 200, 300);
return 0;
}
代码执行:
2. 缺省参数不能在函数声明和定义中同时出现
//a.h
#pragma once
void Func(int a = 10);
//Test.cpp
#include<iostream>
using namespace std;
#include"a.h"
void Func(int a = 20)
{
cout << "a=" << a << endl;
}
int main()
{
Func();
return 0;
}
执行:
如果生命与定义位置同时出现,恰巧两个位置提供的值不同,那编译器就无法确定到底该用那个缺省值。
3. 缺省值必须是常量或者全局变量4. C语言不支持(编译器不支持)
半缺省参数的应用
函数重载
自然语言中,一个词可以有多重含义,人们可以通过上下文来判断该词真实的含义,即该词被重载了。比如:以前有一个笑话,国有两个体育项目大家根本不用看,也不用担心。一个是乒乓球,一个是男足。前者是“谁也赢不了!”,后者是“谁也赢不了。
函数重载概念
函数重载:是函数的一种特殊情况,C++允许在同一作用域中声明几个功能类似的同名函数,这些同名函数的形参列表(参数个数 或 类型 或 类型顺序)不同,常用来处理实现功能类似数据类型不同的问题。
C语言不允许同名函数,CPP可以,但是要求构成函数重载
1、参数类型不同(函数名相同)
//函数名相同,参数不同
#include<iostream>
using namespace std;
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;
}
int main()
{
cout << Add(1, 2) << endl;
cout << Add(1.2, 2.2) << endl;
}
代码执行:
2、参数个数不同
void f()
{
cout << "f()" << endl;
}
void f(int a)
{
cout << "f(int a)" << endl;
}
int main()
{
f();
f(10);
return 0;
}
代码执行:
3.参数类型顺序不同
注意这个顺序不是形参的名字的顺序,是数据类型的顺序
#include<iostream>
using namespace std;
void f(int a, char b)
{
cout << "f(int a,char b)" << endl;
}
void f(char a, int b)//对应上面那个func
{
cout << "f(char a, int a)" << endl;
}
int main()
{
f(10, 'a');
f('a', 10);
return 0;
}
代码执行:
cout与cin等关键字自动识别类型的本质--函数重载
代码实现:
int main()
{
int i = 1, j = 2;
double k = 1.11, l = 2.2;
cout << i<<endl;
cout << k;
}
执行:
不构成函数重载:
1.返回值不同不能构成函数重载
2.不同的命名空间域内
3.函数缺省的函数重载
改正:
代码实现:
void func(int a)
{
cout << "void func(int a)" << endl;
}
void func(int a, int b = 1)
{
cout << "void func(int a,int b)" << endl;
}
int main()
{
func(1,2);//那就是调用上边两个参数的那个函数
//调用存在歧义,不知道调用哪个
//func(1);
return 0;
}
未完待续,感谢来访!