C++

C++语法笔记

语法笔记总结

Posted by wbq on June 9, 2021

最近需要用C++做一些跨平台的组件,我自己本身对C++的语法不是特别熟练,也算是边写边学。 记录了一些平时开发过程中用到的语法笔记。

1.C++支持函数重载,C不支持。底层用了namemangling技术,通过编译器把函数名字改掉,每个方法名不一样。不同编译器有不同的生成规则。

2.extern “C”:

  • 被extern “C” 修饰的代码会按照C语言的方式去编译。

    1
    2
    3
    4
    5
    
    extern "C" {
      	void fun(){
          count << "func()" << endl;
        }
    }
    
  • 如果函数同时有声明和实现,要让函数声明被extern “C” 修饰,函数的实现可以不修饰。

  • 假如有两个函数是通过.c(C语言)的方式进行实现的,那么在cpp的文件中,假如想使用这两个函数的话,不仅要声明,并且需要的在声明外要用 extern “C” 修饰。告诉编译器,这两个方法是用C语言来实现的。

  • 当然也可以这么写

    1
    2
    3
    
    extern "C" {
      	#include "math.h"//include的作用:把math.h的内容原封不动的拷贝一份。
    }
    
  • 那么一个函数声明既要给C调用又要给C++调用怎么办呢?用__cplusplus宏来判断你是否在C++的环境

    1
    2
    3
    4
    5
    6
    7
    8
    9
    
    #ifdef __cplusplus
    extern "C" {
    #endif
        
      //这里是是你的声明或者代码,xxx
        
    #ifdef __cplusplus 
    }
    #endif
    

3.防止重复include:

  • 基于include只是执行了copy操作,所以include多次在语法上都没啥问题,但是代码量会增多,因此为了防止多次include

    可以用以下代码:首次未定义才进入声明逻辑。

1
2
3
4
#ifndef ABC
#define ABC

xxx
  • 或者可以用编译器的特性 #pragma once 来指名头文件只包含一次。

两者区别:第一种不受编译器的任何限制,是标准语法。第二种老编译器不支持,兼容性不够,只针对整个文件。

4.inline内联函数:本质把方法替换成具体实现,可以减少函数调用的开销,会增大代码体积会。但inline只是仅仅是建议编译器进行替换,有时候声明了也不一定起作用,比如:递归。递归是无法内联的。

内敛函数和宏都可以减少函数的开销,对比宏,内敛函数有函数的特性,有代码提示。

5.const:被修饰的变量不可修改

如果修饰的是类或者结构体,那么其中的成员也不可更改。

const修饰右边的东西

1
2
3
4
5
6
7
8
9
10
11
12
13
int age = 10;
int height = 20;

int *p = &age; //int * 是一个int指针 p是具体的值
*p = 20; //修改p指向的具体值
p = &height;  //修改p的值

const int *p1 = &age;// p1是常量,*p1是常量,所以不能改年龄
int const *p2 = &age;// 和p1没区别
int * const p3 = &age;//p3是常量,*p3不是,因此可以年龄。
const int * const p4 = &age;//两个都不能改
int const * const p5 = &age;//两个都不能改
 

6.引用

在C++ 中,引用可以起到跟指针类似的功能,但指针可以修改,引用必须初始化,一旦指向了某个变量,就不可以再改变。

1
2
3
int age = 10
int &refAge = age;
age = 20;

价值:比指针更安全。因为指针在中途可以修改指向,引用不能。

本质:引用是弱化了的指针。本质是指针

7.this

通常一个对象的成员变量是在对象当中的,而函数是放在代码段的,为了在函数体内用this能访问到对象本身,这里有个一个隐式参数,把对象地址传入函数体中,然后将对象地址给this进行赋值

8.堆的申请和释放

new和delete或者new[]和delete[]必须成对出现

1
2
3
4
5
6
7
char *p = new char[4]; //申请连续的4个字节、*p指向第一个。char类型的数组
*p = "11";//给第一个字节赋值
*(p + 1) = "12"//给第二个字节赋值
delete[] p;

memset(p,1,4);//p的4个字节每一个字节都是1

9.析构函数

对象销毁时,进行调用,通过malloc分配的对象free时不会调用析构函数,声明为pulic才能被外界正常使用。

构造方法的顺序和析构的顺序相反。

构造函数: 父类构造->子类构造

析构函数: 子类析构->父类析构

10.多态

定义:同一操作作用于不同的对象,可以有不同的解释,产生不同的执行结果。

方法的调用,默认看指针类型进行操作,不看实际的对象类型是什么。

很多语言默认支持多态,C++要实现多态,需要用到虚函数。

11.虚函数

定义:被virtual修饰的成员函数

只要在父类中声明为虚函数,子类中重写的函数也自动变成虚函数(子类可以省略virtual关键字)

1.有虚函数存在一个类顶部会多4个字节,这个4个字节存放的是虚表(虚函数表)的地址,虚表里面有每个虚函数的方法地址。

2.所有对象共用一张虚表。(如果是多继承就多张虚表)

3.因此如果父类指针指向子类对象,析构函数也应该定义为虚函数。否则释放时无法调用子类的虚函数。

12.模版

模版的本质类似范型,编译器会为多类型的入参生成多个类型的函数。

13.类型转换

static_cast:普通转换,不支持交叉转换。

dynamic_cast:支持交叉转换,会做安全计算。

reinterpret_cast:简简单单的二进制复制。

const_cast:把const转换成非const。

14.nullptr可以解决NULL的二义性(既代表0又代表空指针)

15.智能指针

传统指针存在的问题:

1.需要手动管理内存。

2.容易发生内存泄漏。

3.释放之后产生野指针。

智能指针可以解决这些问题。

实现原理类似于装箱和拆箱,用一个内部私有指针保存对象。在释放时,自动释放私有的内部指针。