C++对C的扩展

文章目录

  • C++对C的扩展
    • ::作用域运算符
      • 代码示例
    • namespace 命名空间
      • 代码示例
      • 命名空间的使用注意
      • 无名命名空间和命名空间别名
      • using声明
    • 语法的增强
      • struct的增强
    • bool 类型
    • 三目运算符
    • const 关键字
      • C语言
      • C++
      • const 替换define
    • 引用
      • 引用作用于数组
      • 引用作为函数参数
      • 引用作为函数返回值
        • 例1:
        • 例2:
      • 引用的本质(C++中无法看到)
      • 指针的引用
      • 常引用
    • 内联函数
      • 内联函数和编译器
        • 类内部的内联函数
        • 内联函数限制
    • 缺省参数和占位参数
      • 缺省参数、
      • 占位参数
    • 函数重载(overlaod)
    • C++和C混合编程

C++对C的扩展

::作用域运算符

通常情况下,如果有两个同名变量,一个是全局变量,一个是局部变量,那么局部变量在其作用域中有较高的优先权, 它将屏蔽全局变量(就近原则)

代码示例

#include <iostream>
#include <string>
#include <iostream>
using namespace std;
int a = 10;
void test()
{int a = 20;cout << a << endl;cout << ::a << endl;
}
int main(int argc, char* argv[])
{test();return 0;
}

namespace 命名空间

在C++中,名称可以是符号常量、变量、函数、结构、枚举、类和对象等。工程越大,名称相互冲突的可能性越大。另外使用多个厂商的类库时,也可能导致名称冲突。为了避免在大规模程序设计中,以及在程序员使用各种各样的C++库的时候,这些表示符的命名发生冲入,标准的C++引入关键字namespace,可以更好的控制标识符的作用域。

代码示例

#include <iostream>
#include <string>
#include <iostream>
using namespace std;
namespace A
{int a = 10;namespace C{int a = 200;}void c(){cout << "A :: C" << endl;}
}
namespace B
{int a = 20;void c();
}
void B::c()
{cout << "B :: C" <<endl;
}
void test()
{cout << A::a << endl;cout << A::C::a << endl;cout << B::a << endl;A::c();B::c();
}
int main(int argc, char* argv[])
{test();return 0;
}

命名空间的使用注意

  • 命名空间只能在全局范围定义

  • 命名空间内可以嵌套命名空间

  • 命名空间是开放的,可以随时把新的成员加入已有的命名空间中

  • 命名空间可以存放变量、函数、类等

  • 命名空间中的函数可以在命名空间外部定义

无名命名空间和命名空间别名

意味着命名空间的标识符只能在本文件内访问,相当于给这个标识符加上了static,使得其可以作为内部连接

#include <iostream>
#include <string>
#include <iostream>
using namespace std;namespace
{int a =300;void func(){cout << " im 啊" <<endl;}
}
namespace vewrylongnamespace
{void func(){cout << "vewrylongnamespace" <<endl;}
}
void test()
{cout << a << endl;func();namespace aa = vewrylongnamespace;aa::func();
}
int main(int argc, char* argv[])
{test();return 0;
}

using声明

  • 表示从using开始,下面的变量使用using声明的命名空间中优先获取,简化对命名空间成员访问的操作。

  • 简化的代价是容易造成命名空间的冲突。

  • using 指明使用具体的命名空间的成员。如果出现冲突则会报错,不会和全局变量冲突。

  • using遇到函数重载的时候,指定函数会对所有的函数起作用。

#include <iostream>
#include <string>
#include <iostream>
using namespace std;
namespace A
{int a = 10;
}
namespace vewrylongnamespace
{int a = 20;
}
void test()
{// int a = 100;using namespace A;// using A::a;cout << a << endl;
}
int main(int argc, char* argv[])
{test();return 0;
}
#include <iostream>
#include <string>
#include <iostream>
using namespace std;
namespace B
{int a = 20;void func(){cout << "B" <<endl;}void func(int a){cout << "func with a" <<endl;}
}
void test()
{using B::func;func(1);
}
int main(int argc, char* argv[])
{test();return 0;
}

注意: 不同命名空间的同名成员使用的时候注意二义性。

语法的增强

  • 全局变量的增强检测
  • 类型检查增强,函数参数和返回值必须有类型,不可以缺省
  • 严格的类型转换
  • struct类型加强

struct的增强

  • C中定义结构体在使用的时候需要加上struct关键字, C++不需要

  • C中的结构体只能定义变量,不能定义成员函数, C++都可以

bool 类型

标准C++的bool类型有两种内建常量(true 和false)表示状态,占一个字节大小,只有两个值

C99之后才有和C++一样的bool值。

三目运算符

C语言三目运算符返回的为数据值,为右值,不能赋值

C++返回的为变量本身(引用), 为左值,可以赋值

const 关键字

C语言

  • const 修饰全局变量,内存空间在文字常量区(属于全局区,只读),不能通过num的地址修改空间内容。
  • const修饰局部变量时,变量名只读,内存在栈区(可读可写),可以通过变量的地址间接修改空间内容。

C++

  • 在C++中一个const不必创建内存空间,是否分配依赖于如何使用;而在C中一个const总是需要一块内存空间。

  • 在函数之外的const默认是内部连接,在其他文件无法访问。如果想在别的文件中使用则必须加extern。

  • C++中对于基础类型,系统不会给data开辟空间,系统会把数据放入符号表中,data可以看成真正的常量。

  • 对data取地址的时候,系统会给data开辟内存空间。

  • 使用一个变量给只读变量初始化的时候也会开辟空间,这种情况下不会将const变量放入符号表中,因此可以修改其值。

  • 对于自定义数据类型,也会开辟空间。

  • 尽量使用const替换宏。

#include <iostream>
#include <string>
#include <iostream>
using namespace std;
void test()
{const int data = 10;cout << data << endl;int *p = (int *)&data;*p = 2000;cout << *p << endl;cout << data << endl;int a = 10;const int b = a;int *pp = (int *)&b;*pp = 200;cout << *pp << endl;cout << b << endl;}
int main(int argc, char* argv[])
{test();return 0;
}

const 替换define

  • const 有类型,可以进行编译器类型安全检查,#define没有类型,不会进行类型检查
  • const有作用域,而#define没有作用域,默认到文件末尾都是有效的

引用

引用是C++对C的重要扩充。在C/C++中,指针的作用基本都是一样的,但是在C++中增加了另外一种给函数传递地址的途径,这就是按引用传递,它也存在于其他语言中,不是C++发明的。

语法:

  • &和别名结合表示引用
  • 给某个变量取别名,就定义某个变量
  • 从上往下替换
int num = 10;
int &a = num; // 表明a是引用变量, a是num的别名

注意:

  • 引用必须初始化
  • 一旦初始化,不能随便修改

引用作用于数组

  • 使用小括号结合
  • 配合typedef
#include <iostream>
#include <string>
#include <iostream>
using namespace std;
void test()
{int arr[5] = {1, 2, 3, 4, 5};int (&a)[5] = arr;cout << a[0]  << a[1] << endl;typedef int ARR[5];ARR aa = {1, 2, 3, 4, 5};ARR &b = aa;cout << b[0]  << b[1] << endl;
}
int main(int argc, char* argv[])
{test();return 0;
}

引用作为函数参数

最常见的是引用作为函数参数和返回值中,当引用被作为函数参数时,在函数内部对引用的任何修改,将对函数外部的参数产生改变。当然可以通过传递一个指针来做和传递引用相同的事情,但是引用具有更清晰的语法。

#include <iostream>
#include <string>
#include <iostream>
using namespace std;
void myswap1(int a, int b)
{int tmp = a;a = b;b = tmp;
}
void myswap2(int *a, int *b)
{int tmp = *a;*a = *b;*b = tmp;
}
void myswap3(int &a, int &b)
{int tmp = a;a = b;b = tmp;
}
int main(int argc, char* argv[])
{int a = 10, b = 20;myswap1(a, b);cout << a << " " << b << endl;myswap2(&a, &b);cout << a << " " << b << endl;myswap3(a, b);cout << a << " " << b << endl;return 0;
}

通过引用参数产生的效果和传地址产生的效果是一样的,引用语法更简单:

  • 函数调用的时候不需要加&符号。
  • 在被调函数中不必在参数前面加*符 引用作为其他变量的别名而存在,因此在一些场合可以代替指针。C++主张用引用传递取代地址传递的方式,因为引用语法容易且不易出错。

引用作为函数返回值

如果从函数中返回一个引用,必须想从函数中返回一个指针一样对待。当函数返回引用时,引用关联的内存一定要存在。

例1:

函数的返回值是引用时候,不要返回局部变量

#include <iostream>
#include <string>
#include <iostream>
using namespace std;
class Person
{
public:Person(string name):name(name){}string name;
};
Person &func1()
{Person num("100");return num;
}
int main(int argc, char* argv[])
{Person &ret = func1();cout << ret.name << endl;return 0;
}

例2:

当函数返回值为左值的时候,函数的返回值类型必须为引用。

#include <iostream>
#include <string>
#include <iostream>
using namespace std;
class Person
{
public:Person(string name):name(name){}string name;
};
Person &func1()
{static Person num("100");// 函数的返回值是引用时候,不要返回局部变量return num;
}
void test()
{func1().name = "200";
}
int main(int argc, char* argv[])
{Person &ret = func1();test();cout << ret.name << endl;return 0;
}

引用的本质(C++中无法看到)

内部实现更像是常量指针

指针的引用

#include <iostream>
#include <string>
#include <iostream>
using namespace std;
void func1(char **p)
{*p = (char *)calloc(1, 32);strcpy(*p, "hello world");
}
void func2(char *&p)
{p = (char *)calloc(1, 32);strcpy(p, "hello world");
}
void test()
{char *p = nullptr;func1(&p);cout << p << endl;free(p);char *p2 = nullptr;func2(p2);cout << p2 << endl;free(p2);
}
int main(int argc, char* argv[])
{test();return 0;
}

常引用

常量引用主要是应用做函数参数,尤其是在类的拷贝/复制构造函数。将函数的形参定义为常量的好处:引用不产生新的变量,减少形参与实参传递时候的开销。由于阴影可能导出实参随形参的改变而改变,将其定义为常量引用可以消除这种副作用。

#include <iostream>
#include <string>
#include <iostream>
using namespace std;
class Person
{
public:Person(string name):name(name){}string name;
};
void test(const Person &p)
{cout << p.name << endl;
}
int main(int argc, char* argv[])
{Person p("aaa");test(p);return 0;
}

注意: 字面量不能赋给引用,但是可以赋值给const引用,const 修饰的引用不能被修改

内联函数

C++从C中继承的重要特征就是效率。假如C++效率明显低于C的效率,那么就会有很大的一批程序员不会去使用C++。在C中我们经常吧一些短并且执行频繁的计算写成宏,而不是函数,这样做的理由是为了 执行效率,宏可以避免函数调用的开销,这些都由预处理来完成。

但是C++出现之后,使用预处理宏会出现两个问题:

  1. 在C中也会出现的问题,宏看起来像一个函数调用,但是会出现一些难以发现的错误。
  2. C++特有问题, 预处理器不允许访问类的成员,也就是说预处理器宏不能用做类的成员函数。

为了保持预处理宏的效率又增加安全性,而且还能像一般程序员函数那样可以在类里访问自如,C++引入了内联函数(inline)

内联函数继承了宏函数的效率,没有函数调用时候的开销,然后又可以像普通函数那样,可以进行参数返回值的类型安全检查,又可以作为成员函数

内联函数和编译器

C++中,预定义宏函数是使用内联函数来实现的,而内联函数本身就是一个真正的函数。内联函数具有普通函数的所有行为。唯一不同的是内联函数会在适当的地方像预定义宏一样展开,所以不需要调用开销。因此应该不适用宏,而使用内联函数。在普通函数前面加上inline使得其编程内联函数。但是必须注意,函数体和声明需要结合在一起,否则编译器会将它作为普通函数来对待。

类内部的内联函数

类内部函数时inline不是必须的,任何类内部定义的简单函数都可能会自动变为内联函数

内联函数限制

  • 不能存在任何形式的循环语句
  • 不能存在过多的判断语句
  • 函数体不能过于庞大,不能对函数进行取址操作

内联仅仅是给编译器一个建议,编译器不一定会接受这个建议,如果你没有将函数声明为内联函数,那么编译器也有可能将函数内联编译。

缺省参数和占位参数

缺省参数、

C++在声明函数原型的时候可以为一个或者多个参数指定默认(缺省)参数值,当函数调用的时候如果没有指定这个值,编译器会自动用默认值代替。

#include <iostream>
#include <string>
#include <iostream>
using namespace std;
int add(int x= 10, int y = 20)
{return x+y;
}
int main(int argc, char* argv[])
{cout << add() << endl;cout << add(50) << endl;cout << add(50, 60) << endl;return 0;
}

注意:

  • 函数默认参数从左到右,如果一个参数设置了默认参数,那么这个参数之后的参数都必须 设置默认参数

  • 如果函数声明和函数定义分开写,函数声明和函数定义不能同时设置默认类型参数

  • 如果函数声明和函数定义分开写,函数定义处的缺省参数时无效的,建议在声明处给默认参数

#include <iostream>
#include <string>
#include <iostream>
using namespace std;
int add(int x = 10, int y = 10);
int main(int argc, char* argv[])
{cout << add() << endl;cout << add(50) << endl;cout << add(50, 60) << endl;return 0;
}
int add(int x, int y)
{return x+y;
}

占位参数

C++在声明函数时,可以设置占位参数。占位参数只有参数类型声明,而没有参数名称声明。一般情况下,在函数体内部无法使用占位参数。

#include <iostream>
#include <string>
#include <iostream>
using namespace std;
int add(int, int, int);
int main(int argc, char* argv[])
{cout << add(50, 60, 100) << endl;return 0;
}
int add(int x, int y, int c)
{return x + y + c;
}

同样的,如果实现也不写参数名,则该参数必须传递,但是无法使用。

重载后置++的时候会用占位参数。

函数重载(overlaod)

同一个函数名在不同场景下可以具有不同的含义,重载使得函数名称使用更方便。

实现函数重载条件:同一个作用域,参数个数不同,参数类型不同,参数顺序不同

#include <iostream>
#include <string>
#include <iostream>
using namespace std;
int add(int, int, int);
int add(int, int);
int main(int argc, char* argv[])
{cout << add(50, 60, 100) << endl;return 0;
}
int add(int x, int y, int c)
{return x + y + c;
}
int add(int x, int y)
{return x + y;
}

注意:

  • 函数的返回值类型不能作为函数重载的依据

  • 函数重载和默认参数在一起的时候会产生二义性问题

为什么返回值不作为重载条件呢?

因为我们在写程序的时候可以忽略函数的返回值,那么直接调用函数的时候编译器无法根据返回值类型来确定调用哪个函数,所以C++中禁止使用返回值类型做重载条件。

编译器在编译重载函数的时候会将重载函数名称修改成自己的函数名称。但是编译器没有统一标准,因此不同的编译器会产生不同的函数名称。

C++和C混合编程

c函数:void myfunc(){},被编译成myfunc

C++函数 void myfunc(){},被编译成:z6myfuncv

由于C++需要支持函数重载,所以C和C++中对同一个函数经过编译之后生成的函数名称是不一样的,这就导致了一个问题,如果在C++中调用一个使用C语言编写模块中的某个函数,那么C++是根据C++的名称修饰方式去查找并且连接这个函数的,这样会发生连接错误。

在C++中包含C库的时候需要将头文件使用extern “C”{}包含起来,这样对应的代码会用C编译器去编译,而不适用C++的方式。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/61145.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

c ++ 学习 之函数重载

在C中&#xff0c;函数重载&#xff08;Function Overloading&#xff09;是一种允许你定义多个同名函数&#xff0c;但它们在参数数量、类型或顺序上有所不同。编译器会根据函数调用时提供的参数信息&#xff0c;自动选择调用匹配的重载函数。函数重载可以使代码更具灵活性&am…

白嫖idea

白嫖idea 地址 https://www.jetbrains.com/toolbox-app/

每日一题:leetcode 1267 统计参与通信的服务器

这里有一幅服务器分布图&#xff0c;服务器的位置标识在 m * n 的整数矩阵网格 grid 中&#xff0c;1 表示单元格上有服务器&#xff0c;0 表示没有。 如果两台服务器位于同一行或者同一列&#xff0c;我们就认为它们之间可以进行通信。 请你统计并返回能够与至少一台其他服务…

jmeter性能测试步骤实战教程

1. Jmeter是什么&#xff1f; 2. Jmeter安装 2.1 JDK安装 由于Jmeter是基于java开发&#xff0c;首先需要下载安装JDK &#xff08;目前JMeter只支持到Java 8&#xff0c;尚不支持 Java 9&#xff09; 1. 官网下载地址&#xff1a; http://www.oracle.com/technetwork/java/…

【java安全】FastJson反序列化漏洞浅析

文章目录 【java安全】FastJson反序列化漏洞浅析0x00.前言0x01.FastJson概述0x02.FastJson使用序列化与反序列化 0x03.反序列化漏洞0x04.漏洞触发条件0x05.漏洞攻击方式JdbcRowSetImpl利用链TemplatesImpl利用链**漏洞版本**POC漏洞分析 【java安全】FastJson反序列化漏洞浅析 …

QT基础 关于QT延迟

目录 QT提供延时 1.自定义延时 2.使用QElapsedTimer 3.使用事件循环 4.跨平台延时 QT提供延时 这里提供四种方法&#xff1a; 1、多线程程序使用QThread::sleep()或者QThread::msleep()或QThread::usleep()或QThread::wait()进行延时处理。 Sleep不会释放对象锁&#x…

Ubuntu20.04安装ROS

Ubuntu20.04安装ROS Excerpt ubuntu安装方式有两种&#xff0c;一种是安装ubuntu系统&#xff0c;另一种是在windows下安装虚拟机&#xff0c;在虚拟机里安装ubuntu。下面为双系统安装ubuntu&#xff08;用虚拟机装ubuntu会很卡&#xff0c;bug很多&#xff0c;除非电脑配置极好…

java八股文面试[多线程]——Happens-Before规则

TODO 知识来源&#xff1a; 【23版面试突击】你知道什么是 happens-before 原则吗&#xff1f;_哔哩哔哩_bilibili 【2023年面试】Happens-Before规则是什么_哔哩哔哩_bilibili

八、MySQL(DML)如何修改表中的数据?

1、修改表数据 &#xff08;1&#xff09;基础语法&#xff1a; update 表名 SET 字段名1数值1,字段名2数值2&#xff0c;…… [where 条件]; &#xff08;2&#xff09; 操作实例&#xff1a; 第一步&#xff1a; 先准备一张表 insert into things values (10086,18,0x12…

【前端代码规范】

前端代码规范 vue3版本:【Vue&React】版本TS版本&#xff1a;【TS&JS】版本vite版本&#xff1a;【Webpack&Vite】版本Eslint版本:命名规则:【见名识意】项目命名&#xff1a;目录命名&#xff1a;JS/VUE文件CSS/SCSS文件命名&#xff1a;HTML文件命名&#xff1a;…

本地电脑搭建Plex私人影音云盘教程,内网穿透实现远程访问

文章目录 1.前言2. Plex网站搭建2.1 Plex下载和安装2.2 Plex网页测试2.3 cpolar的安装和注册 3. 本地网页发布3.1 Cpolar云端设置3.2 Cpolar本地设置 4. 公网访问测试5. 结语6 总结 1.前言 用手机或者平板电脑看视频&#xff0c;已经算是生活中稀松平常的场景了&#xff0c;特…

三组学联合→HiC+Meta+Virome

病毒在微生物死亡率、多样性和生物地球化学循环中发挥着重要作用。地下水是全球最大的淡水&#xff0c;也是地球上最贫营养的水生系统之一&#xff0c;但在这个特殊的栖息地中微生物和病毒群落是如何形成的尚未被探索。本次经典文献分享给大家带来&#xff0c;宏基因组宏病毒组…

RTE_Driver驱动框架和Keil下开发需要支持的xxx_DFP软件包分析

1.RTE_Driver驱动框架 RTE_Driver代表"Run-Time Environment Driver"&#xff0c;是Keil MDK&#xff08;Microcontroller Development Kit&#xff09;中的一个概念。Keil MDK是一种用于嵌入式系统开发的集成开发环境&#xff0c;提供了开发、编译、调试等一系列工具…

绿色能源迎来跨越式增长新时代

当今世界&#xff0c;百年未有之大变局加速演进&#xff0c;新一轮科技革命和产业变革深入发展&#xff0c;全球气候治理呈现新局面&#xff0c;新能源和信息技术紧密融合&#xff0c;生产生活方式加快转向低碳化、智能化&#xff0c;能源体系和发展模式正在进入非化石能源主导…

Briefings in Bioinformatics投稿经验分享

期刊名: BRIEFINGS IN BIOINFORMATICS期刊名缩写:BRIEF BIOINFORM期刊ISSN:1467-5463E-ISSN:1477-40542023年影响因子/JCR分区:9.5/Q1latex模板:http://static.primary.prod.gcms.the-infra.com/static/site/journals/document/oup-authoring-template.zip?node=7987de4…

gcd,辗转相减法,线段树,246. 区间最大公约数

246. 区间最大公约数 246. 区间最大公约数 - AcWing题库 给定一个长度为 N 的数列 A&#xff0c;以及 M 条指令&#xff0c;每条指令可能是以下两种之一&#xff1a; C l r d&#xff0c;表示把 A[l],A[l1],… 都加上 d。Q l r&#xff0c;表示询问 A[l],A[l1],… 的最大公约…

java八股文面试[多线程]——线程间通信方式

多个线程在并发执行的时候&#xff0c;他们在CPU中是随机切换执行的&#xff0c;这个时候我们想多个线程一起来完成一件任务&#xff0c;这个时候我们就需要线程之间的通信了&#xff0c;多个线程一起来完成一个任务&#xff0c;线程通信一般有4种方式&#xff1a; 通过 volat…

gitlab提交项目Log in with Access Token错误

目录 报错信息 问题描述 解决方案 报错信息 问题描述 在提交项目到gitlab时&#xff0c;需要添加账户信息 &#xff0c;但是报了这样一个错&#xff0c;原因应该就是路径问题&#xff0c;我在填写server地址的时候&#xff0c;就出现了路径问题&#xff0c;我把多余的几个/…

拥抱储能新时代!科士达闪耀EESA第二届中国国际储能展览会

2023年8月30日&#xff0c;EESA第二届中国国际储能展览会在苏州国际博览中心拉开帷幕&#xff0c;科士达以“零碳光储数能未来”为主题&#xff0c;亮相G3-20展台&#xff0c;多维度展现户用光储、工商业储能、大型储能等解决方案&#xff0c;彰显安全、高效、可靠的产品性能和…

postgresql类型转换函数

postgresql类型转换函数 简介CAST 函数to_date 函数to_timestamp 函数to_char 函数to_number 函数隐式类型转换 简介 类型转换函数用于将数据从一种类型转换为另一种类型。 CAST 函数 CAST ( expr AS data_type )函数用于将 expr 转换为 data_type 数据类型&#xff1b;Post…