【C++】—— 从 C 到 C++ (上)

07-19 1235阅读

【C++】—— 从 C 到 C++ (上)

  • 一、第一个C++程序
  • 二、命名空间
    • 2.1、命名的烦恼
    • 2.2、命名空间的定义
      • 2.2.1、定义命名空间
      • 2.2.2、命名空间的嵌套
      • 2.2.3、命名空间的同名
      • 2.3、命名空间的使用
        • 2.3.1、指定命名空间的访问
        • 2.3.2、展开命名空间
        • 2.3.3、展开某个成员
        • 2.2.4、总结
        • 三、C++的输入&输出
          • 3.1、输入输出的基本概念
          • 3.2、C++ 的输出
          • 3.3、C++ 的输入
          • 四、缺省参数
            • 4.1、缺省参数的定义
            • 4.2、缺省参数的使用
            • 4.3、全缺省与半缺省
              • 4.3.1、全缺省
              • 4.3.2、半缺省
              • 4.4、注意事项
              • 4.5、缺省参数的应用
              • 五、函数重载

                一、第一个C++程序

                  我们来写一下我们的第一个 C++ 程序:

                #include
                int main()
                {
                	printf("hello world\n");
                	return 0;
                }
                

                  大家是不是很奇怪,这 C++ 怎么一股 C 的味道

                  其实 C++ 是兼容 C 语言的。C++ 本来就是在 C 的基础上增加许多东西。

                  在写 C++ 代码时,要把文件后缀名改为 . c p p .cpp .cpp,这样 VS编译器 就会调用 C++ 的编译器

                  那我们想写一段 C++ 自己的代码怎么写呢?我们来看看。

                #include
                using namespace std;
                int main()
                {
                	cout 
                	
                	printf("%d\n", rand);
                	return 0;
                }
                
                blockquote pimg src="https://i-blog.csdnimg.cn/direct/534303f0d23d42e083cdf40e91127317.png" //p /blockquote p  这是因为在 C语言 中规定在 markfont color="red"同一个域中不能定义同名的东西/font/mark,因为区分不开(C++也是如此)/p p  上述代码中头文件 包含了了库函数 r a n d rand rand ,现在又在全局域中定义了相同名字的变量,就会发生重定义,编译器无法识别你究竟想调用哪个。

                  我们写一个东西,本来写的好好的,但包了一个头文件之后突然就不行了,是不是很不爽

                  注:如果将 r a n d rand rand 变量定义在 m a i n main main 函数中(函数局部域)是不会报错的。因为他们不再同一个域中,而且根据就近原则,当全局域和函数局部域中定义了相同名字的变量时,编译器会直接调用 函数局部域 中定义的那个变量。

                  

                  不仅如此,在工作中,往往是项目组的成员共同协作。小明和小红各自写了一份代码,都没问题,但两人代码一合并,就出现了大量的重命名。怎么办呢?这时小明只能发挥绅士风度,含泪把自己的代码改了。

                  通过上述两个例子,不难发现重命名这事还是很烦的,因此 C++ 中引入了命名空间来解决这个问题

                  

                2.2、命名空间的定义

                • 定义命名空间,需要使用到 n a m e s p a c e namespace namespace 关键字,后面跟命名空间的名字,该名字根据程序员自己的实际需求或喜好定,然后加上一对 { } 即可( { } 后 不需要 加;),{ } 中定义的成员即为命名空间的成员。命名空间中可以定义变量、函数、类型等
                • n a m e s p a c e namespace namespace 本质是定义一个域,这个域跟全局域各自独立,不同的域可以定义同名的变量
                • C++ 中有函数局部域,全局域,命名空间域,类域;域影响的是编译时语法查找一个变量 / 函数 / 类型的出处(声明或定义)的逻辑,所以有了域的隔离,名字冲突就解决了。局部域和全局域会影响编译查找逻辑,还会影响变量的生命周期,命名空间域和类域不影响变量的生命周期
                • n a m e s p a c e namespace namespace 只能定义在全局,当然他也可以嵌套定义
                • 项目工程中多个文件定义的同名 n a m e s p a c e namespace namespace,会认为是一个 n a m e s p a c e namespace namespace,不会发生冲突
                • C++ 标准库都放在⼀个叫 s t d std std( s t a n d a r d standard standard) 的命名空间中。(C++ 官方也怕和别人冲突,嘿嘿)

                    

                  2.2.1、定义命名空间

                    那么现在,我们用命名空间解决上述问题吧

                  #include
                  #include
                  namespace ganyu
                  {
                  	int rand = 10;
                  }
                  int main()
                  {
                  	printf("%d\n", rand);
                  	return 0;
                  }
                  

                    

                  【C++】—— 从 C 到 C++ (上)

                    好消息:能打印了;

                    坏消息:不知道打印了个啥

                    其实,在这里编译器默认打印的是全局域中的 r a n d rand rand。我们将自己定义的 r a n d rand rand 变量放入了命名空间,相当于用一堵墙将其隔开,编译器在默认情况下是不会去命名空间中找的,此时区局域中只有库函数的 r a n d rand rand,程序自然能跑起来了(程序打印的是函数指针)。

                    那如何让编译器去命名空间中找呢?这时我们就需要认识一个新的操作符:: : 类作用域操作符

                  使用方法如下:命名空间名 :: 变量/函数/类型

                  #include
                  #include
                  namespace ganyu
                  {
                  	int rand = 10;
                  }
                  int main()
                  {
                  	//使用::让编译器区命名空间查找
                  	printf("%d\n", ganyu::rand);
                  	return 0;
                  }
                  

                  【C++】—— 从 C 到 C++ (上)

                    

                  2.2.2、命名空间的嵌套

                    在工作中,往往会分为几个部门。每个部门下面又有若干个项目组,每个项目组又有若干个成员,每个项目组会有一个命名空间,该项目组中每个成员也可以在里面嵌套定义自己的命名空间

                  namespace ganyu
                  {
                  	namespace cw
                  	{
                  		struct Node
                  		{
                  			int val;
                  			struct Node* next;
                  		};
                  	}
                  	namespace cq
                  	{
                  		int Add(int x, int y)
                  		{
                  			return x + y;
                  		}
                  	}
                  }
                  

                    

                    变量的访问方法如下:

                  int main()
                  {
                  	struct ganyu::cw::Node newnode = { 0 };
                  	ganyu::cq::Add(10, 20);
                  	
                  	return 0;
                  }
                  

                    可以看到使用了两次:: 操作符来调用

                  注:上述结构体的命名空间指定是加在 N o d e Node Node 前而不是 s t r u c t struct struct 前,因为 s t r u c t struct struct 只是一个关键字

                    

                  2.2.3、命名空间的同名

                    在一个工程项目中,同名的 n a m e s p a c e namespace namespace 不会冲突,他们会认为是一个命名空间。

                  当然,这个合并并不是真正的合并,而是逻辑上的合并

                    即使是自己一个人,一个工程也可能有多个文件,如果每个文件都单独要有一个命名空间,到时自己都记不住。有了这个性质,我们就很方便了

                  S t a c k . h Stack.h Stack.h

                  #pragma once
                  #include
                  #include
                  #include
                  #include
                  namespace ganyu
                  {
                  	typedef int STDataType;
                  	typedef struct Stack
                  	{
                  		STDataType * a;
                  		int top;
                  		int capacity;
                  	}ST;
                  	void STInit(ST* ps, int n);
                  	void STDestroy(ST* ps);
                  	void STPush(ST* ps, STDataType x);
                  	void STPop(ST* ps);
                  	STDataType STTop(ST* ps);
                  	int STSize(ST* ps);
                  	bool STEmpty(ST* ps);
                  }
                  

                  t e s t . c p p test.cpp test.cpp

                  #include"Stack.h"
                  // 全局定义了⼀份单独的Stack
                  typedef struct Stack
                  {
                  	int a[10];
                  	int top;
                  }ST;
                  void STInit(ST* ps) {}
                  void STPush(ST* ps, int x) {}
                  int main()
                  {
                  	// 调⽤全局的
                  	ST st1;
                  	STInit(&st1);
                  	STPush(&st1, 1);
                  	STPush(&st1, 2);
                  	printf("%d\n", sizeof(st1));
                  	// 调⽤ganyu namespace的
                  	ganyu::ST st2;
                  	printf("%d\n", sizeof(st2));
                  	ganyu::STInit(&st2);
                  	ganyu::STPush(&st2, 1);
                  	ganyu::STPush(&st2, 2);
                  	return 0;
                  }
                  

                    

                  2.3、命名空间的使用

                  2.3.1、指定命名空间的访问

                    在前面的代码中,我们访问命名空间中的成员就是用的指定命名空间的访问。通过命名空间加 ::,我们可以让编译器去指定的命名空间访问我们想要访问的成员。这里就不再举例介绍了。

                    

                  2.3.2、展开命名空间

                    通过制定命名空间来访问,好是好,但可能有些小伙伴觉得每次都要使用 :: 操作符,太过繁琐,有没有简便的方法呢?有的,那就是展开命名名间。

                    如果说命名空间是建立了一堵墙,保护里面的成员不被默认访问到,那么展开命名空间就是将这堵墙拆掉

                  命名空间的展开方式如下: u s i n g using using  n a m e s p a c e namespace namespace 空间名;

                  注:虽然定义命名空间后面不用加;,但是展开是要加;

                  #include
                  namespace ganyu
                  {
                  	int a = 0;
                  	int b = 1;
                  }
                  // 展开命名空间中全部成员
                  using namespace ganyu;
                  int main()
                  {
                  	printf("%d\n", a);
                  	printf("%d\n", b);
                  	return 0;
                  }
                  

                    可见,展开命名空间后就不用再使用::操作符啦

                  注:这里的展开命名空间和预处理中的展开头文件并不是一个意思

                  • 展开命名空间可是将命名空间这个域给拆开,可以理解成把这堵墙给推倒
                  • 展开头文件是指头文件的内容拷贝过来

                       但是,凡事有利有弊。既然你把墙给推倒了,那么他就不能起保护作用了,这样又有了变量名相同的风险

                      

                    2.3.3、展开某个成员

                      鲁迅先生说过:“中国人向来是喜欢折中的”

                      既然全展开和不展开都不行,那好,我们折中一下:展开部分成员

                      展开部分成员的方式如下: u s i n g using using 空间名 : : 成员;

                    #include
                    namespace ganyu
                    {
                    	int a = 0;
                    	int b = 1;
                    }
                    using ganyu::b;
                    int main()
                    {
                    	printf("%d\n", ganyu::a);
                    	printf("%d\n", b);
                    	return 0;
                    }
                    

                      

                    2.2.4、总结

                    命名空间的三种使用方式:

                    • 指定命名空间访问:项目中推荐这样方式。(虽然麻烦事麻烦了点,但是基本不会出岔子)
                    • u s i n g using using 将命名空间中的某个成员展开:项目中经常访问的不存在命名冲突的成员推荐这种方式
                    • 展开命名空间中的全部成员:项目中不推荐,冲突风险很大,日常小练习为了方便推荐使用

                      注:展开命名空间后再指定其成员时没问题的,当很少这样

                      三、C++的输入&输出

                      3.1、输入输出的基本概念

                        开头我们写了第一个 C++ 程序,不知大家还记不记得

                      #include
                      using namespace std;
                      int main()
                      {
                      	cout  是  
                             
                              
                               
                               
                                 I 
                                
                               
                                 n 
                                
                               
                                 p 
                                
                               
                                 u 
                                
                               
                                 t 
                                
                               
                              
                                Input 
                               
                              
                            Input  
                             
                              
                               
                               
                                 O 
                                
                               
                                 u 
                                
                               
                                 t 
                                
                               
                                 p 
                                
                               
                                 u 
                                
                               
                                 t 
                                
                               
                              
                                Output 
                               
                              
                            Output  
                             
                              
                               
                               
                                 S 
                                
                               
                                 t 
                                
                               
                                 r 
                                
                               
                                 e 
                                
                               
                                 a 
                                
                               
                                 m 
                                
                               
                              
                                Stream 
                               
                              
                            Stream 的缩写,即标准输入输出流库,定义了标准输入输出对象。可以把他的功能类比成 C 语言中的  
                    • s t d std std:: c o u t cout cout 是 ostream 类的对象,它主要面向窄字符( n a r r o w narrow narrow  c h a r a c t e r s characters characters( o f of of  t y p e type type  c h a r char char))的标准输出流,

                      • 类这个概念当前可先理解成结构体,之后的文章我们会进一步讨论
                      • c o u t cout cout 中的 ‘ c ‘ ‘c‘ ‘c‘ 其实是 c h a r a c t e r s characters characters,表示窄字符的输出;还有一个 w o u t wout wout 表示宽字符的输出
                      • s t d std std:: i n in in 是 i s t r e a m istream istream 类的对象,它主要面向窄字符的标准输入流

                        • 为什么都是字符呢?其实像整型、浮点型等类型只有内存中才有,这是为了内存方便运算;到了文件、网络上就只剩下字符了
                        • 其实无论我们用 p r i n t f printf printf 还是 c o u t cout cout,整型其实都是转换成字符再输出的。输出到哪呢?输出到控制台( c o n s o l e console console)、 L i n u x Linux Linux 中叫终端。如果需要输出到其他地方(如文件)则需要其他方式
                        • 是流提取运算符(当然他们还做位左移/右移)

                        • s t d std std:: e n d l endl endl 是一个函数(很复杂),流插入输出时,相当于插入一个换行字符加插入缓冲区

                          • 注: c o u t cout cout、 c i n cin cin、 e n d l endl endl 都是非常复杂的,它涉及类和对象,运算重载、继承等很多面向对象的知识,我们这里只是极其简单地讲一讲,往后的学习会更加深入了解他
                          • C++ 的输入输出比 C语言 的更加方便,它不需要像 C语言 那样需要手动指定格式,C++ 的输入输出可以自动识别变量类型(本质是通过函数重载实现的),其实最重要的是 C++ 的流能更好的支持自定义类型对象的输入输出

                            3.2、C++ 的输出

                            我们直接上代码来感受一下C++ 的输出

                            #include
                            int main()
                            {
                            	int a = 10;
                            	float b = 2.5f;
                            	char c = 't';
                            	
                            	std::cout 
                            	int a = 10;
                            	float b = 2.5f;
                            	char c = 't';
                            	//cout 
                            	double a = 2.22222222;
                            	printf("%.2lf\n", a);
                            	return 0;
                            }
                            
                            	int a = 0;
                            	float b = 0.0f;
                            	char c = 0;
                            	cin  a  b  c;
                            	cout 
                            	cout 
                            	Func(); // 没有传参时,使⽤参数的默认值
                            	Func(10); // 传参时,使⽤指定的实参
                            	return 0;
                            }
                            
                            	cout 
                            	cout 
                            	STDataType* a;
                            	int top;
                            	int capacity;
                            }ST;
                            void STInit(ST* ps, int n = 4);
                            
                            	assert(ps && n  0);
                            	ps-a = (STDataType*)malloc(n * sizeof(STDataType));
                            	ps-top = 0;
                            	ps-capacity = n;
                            }
                            
                            	cout 
                            	cout 
                            	cout 
                            	cout 
                            	cout 
                            	cout 
                            	;
                            }
                            int fxx()
                            {
                             	return 0;
                            }
                            
                            	cout 
                            	cout 
VPS购买请点击我

文章版权声明:除非注明,否则均为主机测评原创文章,转载或复制请以超链接形式并注明出处。

目录[+]