C++命名空间、输入和输出、缺省参数、函数重载、引用、c++与c相互调用、内联函数

C++

        C++是一种计算机高级程序设计语言,由C语言扩展升级而产生。C语言是结构化和模块化的语言,适合处理较小规模的程序。对于复杂的问题,规模较大的程序,需要高度的抽象和建模时,C语言则不合适。C++引入了类和对象,它既可以进行C语言的过程化程序设计,又可以进行以抽象数据类型为特点的基于对象的程序设计,还可以进行面向对象的程序设计。

        (注:C++的源文件是以.cpp为后缀的,跟C语言不一样。)

命名空间

         C语言中,自己定义的全局变量名或者函数名可能会和库里面的函数一样,产生命名重定义错误。在一个项目中,多人一起完成的时候,大概率也会有命名冲突的问题。

        为了解决这种问题,c++引入了命名空间,也称为域。使用命名空间的目的是对标识符的名称进行本地化,以避免命名冲突,使用namespace关键字添加命名空间。

        namespace后面加一个自定义的名字(不要跟库里的命名冲突),作为以下变量和函数的命名空间域。

        C++使用cout打印数据,头文件是iostream,需要和<<搭配使用,<<是流插入运算符。cout的命名空间是在std中,std是C++标准库的命名空间名字。using namespace std可以展开标准库的命名空间,可以在全局域中找到。

using namespace std;

cout << "hello world";

        编译器在查找变量或者函数的时候,先在当前局部域找,再在全局域找,如果没有指定命名空间,最后是不会去命名空间去找的。指定命名空间:在变量或者函数之前加自己定义的名字::,像上面的dice::time一样。

        一个域中不能出现相同的变量名或者函数名——该域的全局变量不能出现两次,函数中的局部变量不能出现两次。

 

        命名空间可以嵌套定义。

        同一个工程中允许存在多个相同名称的命名空间,编译器最后会合成同一个命名空间中。

namespace dice
{
    namespace game
    {

    }
}

命名空间的三种使用方法

        加命名空间名称及作用域限定符::

        std是c++标准库的命名空间,cout和endl都是c++标准库里面的,使用要在前面指定命名空间。<<是流插入运算符,endl相当于"\n"。(注:c++可以自动识别类型)

#include <iostream>

int main()
{
    // ::是域作用限定符
	std::cout << "hello world" << std::endl;
	return 0;
}

        使用using namespace命名空间名称展开命名空间。

#include <iostream>

using namespace std;

int main()
{
	cout << "hello world" << endl;
	return 0;
}

        使用using将命名空间里的某个成员展开。

#include <iostream>

using std::cout;
using std::endl;

int main()
{
	cout << "hello world" << endl;
	return 0;
}

        using namespace命名空间,直接将这个域全部的变量和函数都放出来到全局域中,这样就和C语言一样了,有命名冲突的可能性。项目中尽量不要使用using namespace std;把c++的标准库直接展开,一般展开常用的和指定命名空间访问。日常练习可以直接using namesapce展开。    

输入和输出

        cin代表标准输入,使用右移运算符 ">>" 实现输入。cout代表标准输出设备,使用左移运算符"<<"实现输出,它们的头文件是<iostream>。endl是特殊的c++符号,表示换行。

        cin和cout是对象不是函数。

        cin和cout不需要像C语言一样指定数据类型输入和输出,可以自动识别类型。

自动识别类型

        c++输入输出更加方便,不用像c语言那样需要指定输入输出格式,c++能够自动识别变量的数据类型。(自动识别类型的本质是函数重载)

缺省参数

        缺省参数是声明或定义函数时为函数的参数指定一个缺省值,调用函数的缺省参数如果没有传参,会使用这个缺省值。 缺省参数只能从右往左依次缺省,中间不能有不是缺省参数的。

        函数有缺省参数,该函数的声明和定义不能同时出现在一个源文件中,函数声明有缺省值,定义也有缺省值,会报错,因为编译器不知道选哪个缺省值作为该函数的缺省值。

        如果需要声明的时候,定义函数的参数不用给缺省值,声明给缺省值即可。

缺省参数分类

全缺省参数

        函数的全部参数都给缺省值。

半缺省参数

       半缺省参数必须从右往左依次给缺省值,中间不能隔开!!

        想要给b缺省值,则必须也给c缺省值。

函数重载

        重载函数是函数的一种特殊情况,为方便使用,C++允许在同一个域中声明或定义多个功能一样的同名函数,但是这些同名函数参数的个数 或类型 或类型顺序必须不同。

        调用函数,根据实参(传参的个数  或传参类型  或类型顺序)选择不同的函数调用。重载函数常用来实现功能一样而处理的数据类型不同的函数。

        不能通过函数返回类型实现函数重载。

        调用函数的时候,根据传的参数自动匹配类型,最后调用对应的函数。

C++和C函数名的修饰规则

        C++和C的函数名修饰规则不同,每一个源文件在汇编执行完后,会产生一个符号表,链接的时候,符号表的合并和重定位。符号表中如果有相同的函数名会报错,C++能够支持函数重载,而C语言不支持函数重载,是因为符号表中的函数名修饰规则不一样。(不同环境函数名修饰规则可能不同)。

        VS2019中不能查看到符号表,在linux环境下查看符号表,具体流程不讲。

C++函数名修饰规则 

        linux环境下,C++的函数名修饰规则:在函数名前面加了_Z3,_Z是前缀,3是函数名的长度,后面加上了函数参数的所有类型。

int Add(int a, int b);

 C函数名修饰规则

        C的函数命名规则:只有函数名。

int Add(int a, int b);

 

引用

        引用是给已经有的变量或者常量起一个别名,语法上引用不会额外开辟空间,它们指向的是同一块空间。对别名进行修改,也会修改原变量。

        变量和引用的地址都是同一个地址,公用同一块空间,标识同一个变量。 

引用特性

        定义引用必须初始化。

        一个变量可以有多个引用。

        引用定义后,不能换成另一个变量的引用。

引用做参数

        引用做参数,功能跟指针差不多,但是不需要解引用。大对象传参的时候,使用引用能提高效率。

 引用做返回值

        调用函数,引用做返回值,返回返回对象的别名,返回变量如果出了该函数的作用域销毁了,再访问该对象,会越界,对这些对象需使用传值返回;只有出了该函数的作用域依旧存在的对象,传引用返回才是正确使用。减少大对象拷贝的时间,提高效率。

        变量接收引用返回对象,将引用返回对象的值赋值给变量,访问变量不会访问到引用返回对象。

        引用接收引用返回对象,这个引用就是返回对象的别名,访问这个引用,就是访问返回对象。第一次访问返回对象还是10,是因为操作系统还没回收这块空间。第二次访问返回对象是随机值,操作系统已经回收了这块空间,并且将这一块的空间都置为随机值。越界访问,是错误的行为,编译器对越界访问是设岗抽查。

        传引用返回,可以直接修改返回对象的值。

引用的使用

        权限可以平移和缩小,但是不能放大, 这是针对指针和引用的。传值传参,形参是实参的拷贝,对形参修改不会改变实参。

// 权限平移
int a = 1;// a可读可修改
int& b = a;// b可读可修改

// 权限放大,编译出错
const int a = 1;// a可读不能修改
int& b = a;// b可读可写,编译出错

// 权限缩小
int a = 1;//a可读可修改
const int& b = a;//b可读不能修改

// 权限放大,编译出错
int& a = 10//10是常量,常量具有常属性,10可读不能修改,要用const修饰

//权限平移
const int& a = 10;//10可读不能修改,a可读不能修改

// 将int类型的值给double类型的值,会产生隐式类型转换,先将a的值放入临时变量中
// 这个临时变量是double类型的,这种临时变量不能修改,相当于被const修饰,再把临时变量赋值给b
int a = 1;
double b = a;

// 权限放大,编译出错
double& c = a;// a产生的临时变量不能修改,c可读可修改
// 正确方式
const double& d = a; // a产生的临时变量可读不能修改,d可读不能修改

        函数使用引用做参数,在不改变参数的情况下,尽量使用const修饰。 

引用的本质

        引用的底层本质上是指针实现的,有开辟空间。观察反汇编,引用和指针,两者是一样的。

引用和指针的区别

        引用的定义必须要初始化,而指针不需要。

        引用语法上不需要开辟空间,指针需要4或者8个字节。

        引用指向的对象不能更改为其它对象,指针可以。

        引用没有空引用,指针有空指针。

        引用加1是数据加1,指针加1是指针往后偏移指向类型的大小。

        引用使用sizeof计算的是引用类型的大小,指针使用sizeof计算,大小是4或者8个字节。

        引用使用上更加方便,指针需要解引用。

C++调用C实现的库

        C语言代码。

// Hello.h
#pragma once
#include <stdio.h>

void Hello();
// Hello.c
#include "Hello.h"

void Hello()
{
	printf("Hello World\n");
}

        C语言代码生成库,在项目属性常规中,将配置类型修改为静态库(.lib),运行程序,就会生成一个静态库。

        静态库(.lib)生成的位置。

        C++程序添加C生成库。

        在项目属性中,链接器常规的附加库目录添加上C生成库的路径;再在输入的附加依赖项加上库名。 

        C++调用C实现的库。以C的函数名修饰规则去找函数。

// test.cpp 在c++操作
// 告诉c++编译器,头文件里的函数使用的是C的库实现的,要用C的函数名修饰规则去找
extern "C"
{
    // 建议直接把头文件添加到编译程序中,再包含头文件
    #include "Hello.h"
}

int main()
{
	Hello();
	return 0;
}

C调用C++实现的库

        C++代码。修改.h文件,将.c改为.cpp,以C的函数名修饰规则修饰函数,重新生成静态库,这个静态库就是C++实现的静态库。(注:此时的C++代码不能出现函数重载)

// Hello.h
#pragma once
#include <stdio.h>

// __cplusplus是c++的宏,c没有。
// 让c++以c语言的方式编译
// 通过条件编译可以让c展开c++实现的头文件的时候去掉extern "C"{}
#ifdef __cplusplus
extern "C"
{
#endif

// 函数声明
void Hello();

#ifdef __cplusplus
}
#endif
// Hello.cpp
#include "Hello.h"

void Hello()
{
	printf("Hello World\n");
}

        C调用C++实现的库。

// test.c 在C的操作
// 包头文件
#include "Hello.h"

int main()
{
	Hello();
	return 0;
}

内联函数

        inline修饰的函数叫做内联函数,编译阶段c++编译器会在调用内联函数的地方展开。在c++中基本不在函数体内使用define定义的宏和常量,尽量使用const、enum、inline替代宏。

inline int Add(int a, int b)
{
    return a + b;
}

内联函数的特性

         内联函数是一种以空间换时间的做法,不用像函数一样建立栈帧,内联函数提升程序运行的效率。内联函数保留了宏函数的优点的同时,解决了宏函数的缺点。宏不能调试,而内联函数可以调试;宏没有类型的检查,可读性差,内联函数有类型的检查。

        inline是起建议的作用,取决于编译器,如果该函数的编译指令较多,编译器也不会让inline起作用,内联函数适用于短小的函数(汇编指令少)。inline修饰一个有一百行代码的函数,调用一千次,inline如果起作用,一百乘一千就是十万行代码,程序暴增;inline不起作用,是一千一百行代码。

        递归不能用inline,编译会忽略。

        内联函数声明和定义不要分离,在头文件只写内联函数的声明,内联函数不会进符号表,如果内联函数所在的源文件跟使用内联函数的源文件不是同一个,会链接失败。内联函数的声明和定义不分离,都写在头文件中。

关键字auto

        使用auto定义变量时必须对其进行初始化,在编译阶段编译器需要根据初始化表达式来推导auto的实际类型。

auto使用场景

        auto能够自动识别类型

auto a = 'a';// auto是char类型
auto b = 1;// auto是int类型
auto c = "abcd";// auto是char*类型
auto* d = &a;// *强调需要指针类型初始化
auto& e = b;// &是变量的别名

        auto能够遍历数组(范围for)

        将a数组的数据自动依次拷贝赋值给b,b是一个变量,自动迭代,自动结束。如果知道auto对应的类型也只可以直接写,这里的auto可以写成int。

         auto通过引用修改数组元素

auto不能使用场景

        auto不能作为函数的参数

        auto不能声明数组

        auto不能通过形参遍历数组

        auto不能同时定义多个变量

空指针nullptr

        在C++中,NULL被定义成了一个宏,#define NULL 0  。NULL变成常量,导致NULL在C++使用在有些情况下会出问题,因此C11引入了空指针nullptr,所以在C++程序中要表示空指针,以后用nullptr。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值