14_C++对c的扩展

发布时间 2023-10-05 21:44:58作者: 爱吃冰激凌的黄某某

c++对c的扩展

::作用域运算符

::使用全局变量

using namespace std;
int a = 10;
void test01()
{
  int a = 20;
  cout << a << endl; //20
  cout << ::a << endl; //10
}

命名空间 namespace

namespace和c语言中static只在本源文件中有效差不多

命名空间使用语法

创建一个命名空间

namespace A{
  int a = 10;
}
namespace B{
  int a = 20;
}
void test(){
  cout << A::a << endl; //10
  cout << B::a << endl; //20
}

命名空间可嵌套

namespace A{
  int a = 10;
  namespace B{
    int a = 20;
  }
}
void test(){
  cout << A::a << endl; //10
  cout << A::B::a << endl; //20
}

命名空间是开放的,可以将新成员添加入命名空间

namespace A{
  int a = 10;
}
namespace A{
  void func(){
    cout << "hello namespace!" << endl;
  }
}
void test(){
  cout << A::a << endl;
  A::func();
}

声明和实现可分离

namespace MySpace{
  void fun1();
  void fun2();
}
void MySpace::fun1(){
  cout << "MySpace::fun1" << endl;
}
void MySpace::fun2(){
  cout << "MySpace::fun2" << endl;
}

无名命名空间

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

namespace{
  int a = 10;
  void func(){
    cout << "hello namespace" << endl;
  }
  void test(){
    cout << "a : " << a << endl;
    func();
  }
}

命名空间别名

namespace veryLongName{
  int a = 10;
  void func(){
    cout << "hello namespace" << endl;
  }
}
void test(){
  namespace shortName = veryLongName;
  cout << "veryLongName::a" << shortName::a << endl;
  veryLongName::func();
  shortName::func();
}

using 声明命名空间中的成员 可用

使用using声明后可以直接用 A::a -> a

namespace A{
  int paramA = 20;
  itn paramB = 30;
  void funcA(){
    cout << "hello funcA" << endl;
  }
  void funcB(){
    cout << "hello funcB" << endl;
  }
}
void test(){
  cout << A::paramA << endl;
  A::fucA();
  
  //using声明成员可用
  using A::paramA;
  using A::funcA;
  cout << paramA << endl;
  funcA();
}

using声明成员碰到函数重载

using声明包含函数重载

namespace A{
  void func(){}
  void func(int x){}
  void func(int x, int y){}
}
void test(){
  using A::func;
  func();
  func(10);
  func(10, 20);

using声明整个命名空间

using声明整个命名空间不会和局部变量发生冲突, 但会与其他命名空间发生冲突

namespace A{
  int paramA = 20;
  int paramB = 30;
  void funcA(){
    cout << "hello funcA" << endl;
  }
  void funcB(){
    cout << "hello funcB" << endl;
  }
}
void test1(){
  using namespace A;
  cout << paramA << endl;
  cout << paramB << endl;
  funcA();
  funcB();
  
  //不会和局部变量发出冲突
  int paramA = 10;
  cout << paramA << endl;
}
namespace B{
  int paramA = 20;
  int paramB = 30;
  void funcA(){
    cout << "hello funcA" << endl;
  }
  void funcB(){
    cout << "hello funcB" << endl;
  }
}
void test2(){
  using namespace A;
  using namespace B;
  cout << paramA << endl; //error 发生冲突
}

struct类型增强

c中定义结构体变量需要加上struct关键字,c++不需要。c中的结构体只能定义成员变量,不能定义成员函数。c++即可以定义成员变量,也可以定义成员函数。

结构体中即可以定义成员变量, 也可以定义成员函数

struct Student{
  string mName;
  int mAge;
  void setName(string name){
    mName = name;
  }
  void setAge(int age){
    mAge = age;
  }
  void showStudent(){
    cout << "Name:" << mName << " Age:" << mAge << endl;
  }
}

c++中定义结构体变量不需要加struct关键字

void test01(){
  Student student;
  student.setName("John");
  student.setAge(20);
  student.showStudent();
}

bool类型关键字

标准c++的bool类型有两种内建的常量true(转换为整数1)和false(转换为整数0)表示状态。这三个名字都是关键字。bool类型只有两个值, true(1值), false(0值) bool类型占1个字节大小 给bool类型赋值时,非0值会自动转换为true(1),0值会自动转换false(0)

void test(){
  cout << sizeof(false) << endl; //1 因为bool类型占一个字节
  bool flag = true;
  flag = 10; //非0值自动为true, 0值为false
}

引用(reference)

在c/c++中指针的作用基本都是一样的,但是c++增加了另外一种给函数传递地址的途径,这就是按引用传递(pass-by-reference)。变量名实质上是一段连续内存空间的别名,是一个标号(门牌号)程序中通过变量来申请并命名内存空间通过变量的名字可以使用存储空间1对一段连续的内存空间只能取一个别名吗? c++中新增了引用的概念,引用可以作为一个已定义变量的别名。

引用的定义

本质: 给变量取个别名

定义步骤:

1&别名

2给哪个变量取别名, 就定义该变量

3从上往下整体替换

普通变量的引用

void test01(){
    int a = 10;
    int &b = a; //别名b, 不开辟额外空间
}

image-20230923173924777

数组的引用

int arr[5] = {10, 20, 30, 40, 50};
int n = sizeof(arr)/sizeof(arr[0]);
int (&myarr)[5] = arr;
for(int i=0;i<n;i++)
{
    cout << myArr[i] << " ";
}
cout << endl;

指针变量的引用

int num = 10;
int *p = &num;
int *(&myp) = p;
cout << *p << endl;
cout << *myp << endl;

函数的引用

void fun01()
{
    cout << "fun01" << endl;
}

void (&myfun)() = fun01;
myfun();

引用作为函数的参数

函数内部可以通过引用操作外部变量

void swap01(int *p1, int *p2)
{
    int tmp = *p1;
    *p1 = *p2;
    *p2 = tmp;
}
void swap02(int &x, int &y)
{
    int tmp = x;
    x = y;
    y = tmp;
}
int main()
{
    int a = 10;
    int b = 20;
    cout << a << ", " << b << endl; //10, 20
    //swap01(&a, &b);
    swap02(a, b);
    cout << a << ", " << b << endl; //20, 10
}

引用作为函数返回值

不要返回普通局部变量的引用

int& getData()
{
	int num = 10;
    return num; //返回时局部变量已释放
}
int main()
{
    int &b = getData(); //操作已释放的变量有问题
}

返回值类型为引用 可以完成链式操作

struct Stu
{
    Stu& printStu(Stu &ob, int value)
    {
        cout << value << " ";
        return ob;
    }
};
int main()
{
    Stu ob1;
    ob1.printStu(ob1, 100).printStu(ob1, 200).printStu(ob1, 300); //100 200 300
}

常饮用

给常量取别名, 不能通过常饮用 修改内容

//int &a = 10; //error
const int &a = 10;
a = 10;//error

常饮用 作为 函数参数: 防止函数内部修改外部的值

void printInt(const int &a)
{
    //a = 200; error
    cout << a << endl;
}
int main()
{
    int num = 100;
    printInt(num); //100
}

内联函数

声明内联函数

内联函数必须在定义时使用inline, 不能在声明时使用inline

int my_add(int x, int y);
int main()
{
    cout << myAdd(100, 200) << endl;
}
//内联函数
inline int myAdd(int x, int y)
{
    return x+y;
}

特点: 在编译阶段 将内联函数中的函数体 替换函数调用处, 避免函数调用时的开销

宏函数和内联函数区别

宏函数和内联函数 都会在适当的位置 进行展开 避免函数调用开销。

宏函数在预处理阶段展开,内联函数在编译阶段展开.

宏函数的参数没有类型,不能保证参数的完整性,内联函数的参数有类型 能保证参数的完整性。

宏函数没有作用域的限制,不能作为命名空间、结构体、类的成员内联函数有作用域的限制,能作为命名空间、结构体、类的成员

内联函数的注意事项

在内联函数定义的时候加inline修饰

类中的成员函数 默认都是内联函数(不加inline 也是内联函数)

有时候就算加上inline也不一定是内联函数(内联函数条件)

不能存在任何形式的循环语句

不能存在过多的条件判断语句

函数体不能过于庞大

不能对函数取地址

有时候不加inline修饰也有可能是内联函数。

内不内联 由编译器决定。

函数重载

函数重载底层实现

void func(){}
void func(int x){}
void func(int x, char y){}

以上在linux生成的编译后的函数名为

_Z4funcv //v 代表void,无参数
_Z4funci //i 代表参数为int
_Z4funcic //i 第一个参数int, c 第二个参数char

函数的默认参数

void TestFunc01(int a = 10, int b = 20){
    cout << a+b << endl;
}

注意点:

1一个参数设置默认值, 它后面的参数也要设值

void TestFunc02(int a, int b = 10, int c = 20){}

2如果函数声明和函数定义分开, 函数声明设置了默认参数, 函数定义不能再设置默认参数

void TestFunc03(int a = 0, int b = 0);
void TestFunc03(int a, int b){}

默认参数和函数重载同时出现时注意二义性

void func(int x){}
void func(int x, int y = 10){}
int main(){
    func(10); //error
}

占位参数

声明函数时, 可以设置占位参数, 只有参数类型没有参数名.

一般情况下, 在函数体内部无法使用占位参数

void TestFunc01(int a, int b, int){
	//函数内部无法使用占位参数
}

占位参数也可以设置默认值

void TestFunc02(int a, int b, int = 20){
    //函数内部任然无法使用占位参数
}
void main()
{
    TestFunc01(10, 20); //error 占位参数也要传值
    TestFunc02(10, 20); //ok
}

extern "C"浅析

C++调用C语言代码

以下在Linux下测试:

c函数: void MyFunc(),被编译成函数:MyFunc

C++函数:void MyFunc(){}.被编译成函数:_Z6Myfuncv

通过这个测试,由于c++中需要支持函数重载,所以c和c++中对同一个函数经过编译后生成的函数名是不相同的,这就导致了一个问题,如果在c++中调用一个使用c语言编写模块中的某个函数,那么c++是根据c++的名称修饰方式来查找并链接这个函数,那么就会发生链接错误,以上例, c++中调用MyFunc函数,在链接阶段会去找Z6Myfuncv,结果是没有找到的,因为这个MyFunc函数是c语言编写的,生成的符号是MyFunc。那么如果我想在c++调用c的函数怎么办?

extern"C"的主要作用就是为了实现c++代码能够调用其他c语言代码。加上extern "C"后,这部分代码编译器按c语言的方式进行编译和链接,而不是按c++的方式。

fun.h

#ifndef MYMODULE_H
#define MYMODULE_H
#include<stdio.h>
#if __cplusplus
extern "C"{
#endif
    extern void func1();
    extern int func2(int a, int b);
#if __cplusplus
}
#endif
#endif

fun.c

#include<stdio.h>
#include"fun.h"

void func1(){
    printf("hello world!");
}
int func2(int a, int b){
    return a+b;
}

main.cpp

#include<iostream>
#include "fun.h"
using namespace std;
int main(){
    func1();
    cout << func2(10, 20) << endl;
    return 0;
}