C艹笔记--面向对象程序设计


C艹面经知识点
【C→C++】打开C++世界的大门
【C进阶】——我们写的代码是如何一步步变成可执行程序(.EXE)的?
在这里插入图片描述
C++内存泄漏问题
C++内存泄漏及解决问题
C++内存泄露
内存泄漏,是指在程序代码中动态申请的、堆上的内存 由于某种原因、在使用后没有被释放,进而造成内存的浪费。
最简单的解释就是,主动申请的内存块在使用后没有被释放。

malloc/new申请的内存没有主动释放

  • malloc 申请的内存要主动调用 free,new 申请的内存要主动调用 delete,否则就会导致内存泄漏。
    注意:普通指针和数组指针的free/delete方式不同.
  • malloc/free以及new/delete必须是各自成对出现,如果混用,就会导致意想不到的情况出现。另外,如果用delete释放void指针指向的对象同样也会造成内存泄露。

栈通过指针调用堆存储的公用函数。
在这里插入图片描述

一个C++程序从源代码到执行文件,有四个过程:

一.预编译

(1)将所有#define删除,并且展开所有宏定义

(2)处理所有条件预编译命令,如#if, #ifndef

(3)处理#include预编译命令,将被包含的文件插入到该预编译命令的位置

(4)过滤所有注释

(5)添加行号和文件名标识

二.编译

(1)词法分析,将源代码字符序列分割成一系列记号

(2)语法分析,对词法分析的结果进行语法分析,产生语法树

(3)语义分析,判断表达式是否有意义

(4)代码优化

(5)生成目标代码(汇编)

(6)目标代码优化

三.汇编

将汇编代码转换成机器可以执行的指令

四.链接

将不同的源文件产生的目标文件进行链接,从而形成一个可执行的程序
五.运行
main函数的作用
main函数是程序的唯一入口。
也就是说,程序运行时,首先从main函数开始执行。
一个程序,必须要有一个main函数,而且也只能有一个main函数。

一般来说:C艹类中定义的成员变量大小和多少决定其实例化的每个对象内存数量级,因为方法函数都是公用的只有成员变量是每个对象私有的。

虚函数表与内存详解
C++程序的内存格局通常分为五个区:全局数据区(data area),代码区(code area)、栈区(stack area)、堆区(heap area)(即自由存储区),文字常量区。全局数据区存放全局变量和静态变量,初始化的全局变量和静态变量在一块区域,未初始化的全局变量和未初始化的静态变量在相邻的另一块区域,程序结束后由系统释放。;所有类成员函数和非成员函数代码存放在代码区;为运行函数而分配的局部变量、函数参数、返回数据、返回地址等存放在栈区;文字常量区存储常量字符串,程序结束后由系统释放,余下的空间都被称为堆区。
在这里插入图片描述

开发环境简介

Linux 下C++开发入门指南,简易入门版
现在C++的主流开发平台是linux,所以很多人就说vs让开发者缺乏对C++执行过程的了解,linux上没有像vs那样强大的集成开发环境,比较分散,开发人员需要自己调度各种资源。

Linux下的开发,一般都是基于开源的编译器,很多时候并没有太好的IDE,此时非常有必要掌握一门通用的编译构建方法。Makefile应运而生,成功了最流行的Linux下的编译构建方法。Makefile主要是面向代码的编译构建,但是其目标依赖执行的基本原理,也可以用到其他有类似的场景。

类与对象简介

类需要实例化对象才能起作用。
样例:
在这里插入图片描述
关键字class表示类,一般首字符用大写字母表示,以示与对象名的区别。关键字public和protected(或private)表示访问控制。在类中说明的,要么是数据成员,要么是成员函数。它们或者说明为public的,或者说明为protected的,或者说明为private的。类具有封装性,它可以说明哪些成员是public的,哪些不是。说明了protected的成员,外部是不能访问的.

但是可以通过类的public成员函数间接地访问。

类与结构的区别

  • C++中,结构是用关键字struct声明的类,默认情况下其成员是公共(public)的。
  • C++中,默认情况下类(class)定义中的成员是private的。

各种声明定义的作用范围在{中}

各种声明定义的作用范围为其最直接被包括的{}.
例如:命名空间,全局/静态/局部变量等等。

定义变量

静态变量和局部变量的作用范围为其最直接被包括的{}.
静态变量有效范围是所有,局部变量有效范围是某个。
当全局变量和局部变量名字相同时,局部优先。

定义成员函数

∷叫作用域区分符,指明一个函数属于哪个类或一个数据属于哪个类。∷可以不跟类名,表示全局数据或全局函数(即非成员函数)
类内定义成员函数
在这里插入图片描述

类外定义成员函数

类名加在成员函数名之前而不是加在函数的返回类型前

将类定义和其成员函数定义分开,是目前开发程序的通常做法。我们把类定义(头文件)看成类的外部接口,类的成员函数定义看成类的内部实现。将类拿来编制应用程序时,只需类的外部接口(头文件)。这和我们使用标准库函数的道理是一样的,即只需包含某函数声明的头文件。因为类定义中全部包含了类中成员函数的声明。
在这里插入图片描述

重载与覆盖的区别

重载与覆盖的不同

  • 重载的几个函数必须在同一个类中
  • 覆盖的函数必须在有继承关系的不同的类中
  • 覆盖的几个函数必须要求函数名、参数、返回值都相同;
  • 重载的函数必须函数名相同,参数不同。参数不同的目的就是为了在函数调用的时候编译器能够通过 参数来判断程序是在调用的哪个函数。这也就很自然地解释了为什么函数不能通过返回值不同来重载,因为程序在调用函数时很有可能不关心返回值,编译器就无法从代码中看出程序在调用的是哪个函数了。
  • 覆盖的函数前必须加关键字Virtual

重载和Virtual没有任何瓜葛,加不加都不影响重载的运作。

虚函数(Virtual)

C艹虚函数类似Java抽象函数
虚函数的作用和原理(看评论)
虚函数原理详解
虚函数表与内存详解
1、虚函数的作用:简单讲即实现多态。
基类定义了虚函数,子类可以重写该函数,当子类重新定义了父类的虚函数后,父类指针根据赋给它的不同的子类指针,动态地调用属于子类的该函数,且这样的函数调用是无法在编译器期间确认的,而是在运行期确认,也叫做迟绑定。
2、实现原理:虚函数表+虚表指针
我们知道,当一个类(A)继承另一个类(B)时,类A会继承类B的函数的调用权。所以如果一个基类包含了虚函数,那么其继承类也可调用这些虚函数,换句话说,一个类继承了包含虚函数的基类,那么这个类也拥有自己的虚表。

也就是说,每一个类都有一份虚表,不管多少个对象,都是用的是同一个虚表

虚函数的作用是使基类指针指向派生类对象时,访问派生类的同名函数,实现动态联编。
原理:每个含有虚函数的派生类对象都有一个或多个(多继承)虚函数表指针,虚函数表指针指向了一个虚函数表,该表中每个表项存放该派生类的虚函数地址,如果该类覆写了基类的虚函数,则存放派生类的虚函数地址,如果未覆写,则存放基类的虚函数地址。

虚函数的作用:允许在派生类中重新定义与基类同名函数,并且可以通过基类指针或引用来访问基类或派生类同名函数
每个虚函数都在vtable中有一个表项,保存着一条跳转到他的入口地址的指令。
当一个虚函数的对象被创建的时候,他在头部附加一个指针,指向vtable中的相应位置,调用虚函数的时候,不管你是用什么指针调用的,都根据vtable找到入口地址再执行,从而实现了动态联编,而不是像普通函数那样简单的跳转到一个固定的地址。

继承

C++的三种继承方式详解
C++继承详解
C++继承祥祥祥解
在实际运用中一般使用的都是public继承,几乎很少使用protected/private继承,也不提倡使用protected/private继承,因为protected/private继承下来的成员都只能在派生类的类里面使用,实际中扩展维护性不强。
在这里插入图片描述
在这里插入图片描述

1.基类private成员在派生类中无论以什么方式继承都是不可见的。(注意:这里的不可见是指基类的私有成员还是被继承到了派生类对象中,但是语法上限制派生类对象不管在类里面还是类外面都不能去访问它
2.如果基类成员不想在类外直接被访问,但需要在派生类中能访问,就定义为protected。
3.上述表格实质有个规律:基类的私有成员在派生类中都是不可见的,基类的其他成员在派生类中的访问方式为Min(成员在基类的访问限定符,继承方式)(这里的大小关系为public > protected > private)。

样例
在这里插入图片描述

继承小总结

  • 派生类继承基类的方式,决定基类中成员在派生类中的最高权限(高于则降权限)。
  • 派生类中的成员函数可以直接访问基类中的public和protected成员,但不能直接访问基类的private成员。
  • 通过派生类的对象访问从基类继承的成员,只能访问public成员。

C++中类中为继承而生的访问级别:protect

-C++面向对象中的访问级别除了public、private外,还有protect
-protect修饰的成员不能被外界所访问
-protect修饰的成员能被子类直接访问
-protect关键字是为了继承而专门设计的
-没有protect就无法完成真正意义上的代码复用

若派生类想调用基类不可访问成员,只能通过调用基类可访问成员方法间接调用基类不可访问成员。

C++中::和:, .和->的作用和区别

符号::和:的作用和区别

::
  • ::是作用域运算符,A::B表示作用域A中的-名称B,A可以是命名空间、类、结构;
  • 类作用域操作符
  • “::”指明了成员函数所属的类。如:M::f(s)就表示f(s)是类M的成员函数。
    作用域,如果想在类的外部引用静态成员函数,或在类的外部定义成员函数都要用到。
    使用命名空间里的类型或函数也要用到(如:std::cout, std::cin, std::string 等等)
  • 表示“域操作符”
    例:声明了一个类A,类A里声明了一个成员函数void f(),但没有在类的声明里给出f的定义,那么在类外定义f时, 就要写成void A::f(),表示这个f()函数是类A的成员函数。
  • 表示引用成员函数及变量,作用域成员运算符
    例:System::Math::Sqrt() 相当于System.Math.Sqrt()
:一般用来表示继承
  1. 表示结构内位域的定义(即该变量占几个bit空间)
typedef struct _XXX{

unsigned char a:4;

unsigned char c;
};XXX
  1. 在构造函数后面,冒号起分割作用,是类给成员变量赋值的方法,初始化列表,更适用于成员变量的常量const型。
    构造函数,就是与类同名的函数,它与普通函数的区别在于,它没有返回类型。在构造函数后面紧跟着冒号加初始化列表,各初始化变量之间以逗号(,)隔开。
    例如:
class myClass
{
public :
    myClass();// 构造函数,无返回类型,可以有参数列表,这里省去
    ~myClass();// 析构函数
    int a;
    const int b;
}

myClass::myClass():a(1),b(1)// 初始化列表
{
}

关于“:”冒号的用法的说明:

  • 初始化列表的作用相当于在构造函数内进行相应成员变量的赋值,但两者是有差别的。

在初始化列表中是对变量进行初始化,而在构造函数内是进行赋值操作。两都的差别在对于像const类型数据的操作上表现得尤为明显:const 类型的变量必须在定义时进行初始化,而不能对const型的变量进行赋值,因此const类型的成员变量只能(而且必须)在初始化列表中进行初始化,如:

myClass::myClass()
{
    a = 1;// 没错,效果相当于在初始化列表中进行初始化
    b = 1;// 出错,const变量不能进行赋值操作;
}
2) 初始化的顺序与成员变量声名的顺序相同。
先看一下下面的程序:
myClass::myClass():b(1),a(b)
{
}

这样的执行结果a,b各是多少呢?b=1,a=1?不是,b=1而a是个随机数。这一点是相当重要,一般在初始化列表中进行初始化时,初始化的顺序应与声明的顺序保持一致,防止出现不必要的错误。

  • public:和private:后面的冒号,表示后面定义的所有成员都是公有或私有的,直到下一个"public:”或"private:”出现为止。"private:"为默认处理。
  • 类名冒号后面的是用来定义类的继承。
class 派生类名 : 继承方式 基类名
{

派生类的成员

};

这里的冒号起到的就是声名基类的作用,在基类类名前面可以加public/private/protected等标签,用于标识继承的类型,也可以省略, 省略的话,用class定义的类默认为private,用struct定义的类默认为public
与初始化列表一样的,这里也可以声名多个基类,各基类之间用逗号(,)隔开。

  • 条件语句(? : )

与?构成条件语句,作用相当于if else,如下;

int a,b,c;
a=3;
b=2;
c=a>b?a:b;// 如果a>b成立,则反a赋给c,否则把b赋给c

条件语句的结构为:
条件表达式?表达式1:表达式2
当条件表达式为true时,表达式的值为表达式1的值,否则为表达式2的值。

注意:
1)?:可以嵌套,但不推荐使用(难懂),例如:

int max = i>j ? i>k ? i : k : j>k ? j : k;

2)?:具有很低的优先级,例如:

int i = 3;
int j = 2;
cout << i>j?i:j;// 出错,<<比>具有更高的优先级,执行顺序为 ((cout<<i)>j)?i:j,相当于是比较cout<<i与j的大小,然后根据比较结果决定表达式值为i或j,这 显然要出错的,cout<<i的值是cout,不能跟整型数j进行比较。
cout << (i>j)?i:j;//输出1或0,相当于(cout<<(i>j))作为判决条件,来决定表达式的值为i或j,而 cout<<(i>j),i>j则输出1否则0,然后再将(cout<<(i>j))作为?:的条件,如果 cout正确执行则为1(true),否则为0(false),以此决定表达式值为i或j
cout <<(i>j?i:j);//i>j则输出i,否则输出j,表达式值为true如果cout正确执行,否则为false
  • 语句标签

通常跟goto配合使用,如:

step1: a = f1();
....
goto step1;

这种作法也不是很推荐,原因在于它破坏了语句的顺序执行;

  • switch语句中case后。
符号.和->的作用和区别

A.B则A为对象或者结构体; 点号(.):左边必须为实体。
A->B则A为指针,->是成员提取,A->B是提取A中的成员B,A只能是指向类、结构、联合的指针; 箭头(->):左边必须为指针;

->的优先级比较高==(*指针).
class A
{
public:
    int a = 0;
};
int main()
{
    A b;
    A *p = &b;
    b.a; //类类型的对象访问类的成员
    p->a; //类类型的指针访问类的成员
}

#include

C++头文件和cpp文件的原理
#include 是一个来自C语言的宏命令,它在编译器进行编译之前,即在预编译的时候就会起作用。#include的作用是把它后面所写的那个文件的内容,完完整整地、一字不改地包含到当前的文件中来。值得一提的是,它本身是没有其它任何作用与副功能的,它的作用就是把每一个它出现的地方,替换成它后面所写的那个文件的内容。简单的文本替换,别无其他
#include 一个文件(无论.h或是.cpp),仅仅是将文件中的代码复制一份到你的代码之中,头文件(.h)仅在编译过程中的预处理中起到作用。
注意:只有cpp文件可以include,因为头文件只能声明不能有定义。
h文件不能用include,因为include的头文件会链接cpp文件到h文件中。
反之若是h文件未被cpp定义仅仅声明则可以被h文件include。

#include""和#include<>区别

二者区别如下(开始搜索的位置不同):
  1、“”是表示从当前目录开始搜索,然后是系统目录和PATH环境变量所列出的目录。
  2、<>表示从系统目录下开始搜索,然后再搜索PATH环境变量所列出的目录,不搜索当前目录。
  正常情况下,自行定义的头文件应该用"",因为这些文件放在工程目录(也就是编译器的当前目录)下,而不是放在公共头文件目录下。
而系统提供的头文件,比如库函数的头文件,可以用<>。
这样效率最快。

.h文件(声明)和.cpp文件(定义)

关于C++ .h文件和.cpp文件的知识梳理
通常,在一个C++程序中,只包含两类文件——.cpp文件和.h文件。

  • .cpp文件被称作C++源文件,里面放的都是C++的源代码
  • .h文件则被称作C++头文件,里面放的也是C++的源代码,头文件不用被编译

注意这里提到了两个概念,一个是“声明”,一个是“定义”。简单地说:

  • “声明”则只是声明这个符号的存在,即告诉编译器,这个符号是在其他文件中定义的,我这里先用着,你链接的时候再到别的地方去找找看它到底是什么吧。声明的时候就只需要写出这个符号的原型了。
  • “定义”就是把一个符号完完整整地描述出来:它是变量还是函数,返回什么类型,需要什么参数等等。定义的时候要按C++语法完整地定义一个符号(变量或者函数)

需要注意的是,一个符号,在整个程序中可以被声明多次,但却要且仅要被定义一次
h文件不能用include,因为include的头文件会链接cpp文件到h文件中。
(反之若是h文件未被cpp定义仅仅声明则可以被h文件include)

仅在.h 文件中进行变量和函数的声明而不进行定义(存在一些特例,比如class 的内敛成员函数,模板函数), 因为函数与变量的声明可以进行任意多次,而定义只能有一次。

头文件不用被编译。我们把所有的函数声明全部放进一个头文件中,当某一个.cpp源文件需要它们时,它们就可以通过一个宏命令#include包含进这个.cpp文件中,从而把头文件的内容合并到.cpp文件中去。当.cpp文件被编译时,这些被包含进去的.h文件的作用便发挥了。

函数声明和定义的分离十分简单,我们只需要在.h文件中写出函数返回值类型函数名、接受的参数,再到.cpp文件中去写函数的具体实现就行了。而变量的声明和定义的分离就要靠关键字extern 了。extern 主要有两个作用,其中一个是对指定单元强制使用C 的编译器进行编译,另一个便是声明变量的作用范围的关键字,其声明的函数和变量可以在本编译单元或其他编译单元中使用。
总结:当你需要定义一个变量给所有include 该头文件的单元共同使用时,你应该在.h文件中使用extern 关键字,表示该变量将在include这个头文件的编译单元中使用(即进行“声明”)。同时另外新建一个.cpp文件对该头文件中声明的变量进行统一定义(定义变量的文件实际上可以是任意include该头文件的.cpp文件,但为了避免混乱,我们通常约定是在和头文件同名的.cpp文件中进行定义,可以认为是一种规范)。

头h文件中的保护措施(防止同一文件多次包含同一头文件)

考虑一下,如果头文件中只包含声明语句的话,它被同一个.cpp文件包含再多次都没问题——因为声明语句的出现是不受限制的。然而,上面讨论到的头文件中的三个例外也是头文件很常用的一个用处。

那么,一旦一个头文件中出现了上面三个例外中的任何一个,它再被一个.cpp包含多次的话,问题就大了。因为这三个 例外中的语法元素虽然“可以定义在多个源文件中”,但是“在一个源文件中只能出现一次”。

设想一下,如果a.h中含有类A的定义,b.h中含有类B的定义,由于类B的定义依赖了类A,所以b.h中也#include了a.h。现在有一个源文件,它同时用到了类A和类B,于是程序员在这个源文件中既把a.h包含进来了,也把b.h包含进来了。

这时,问题就来了:类A的定义在这个源文件中出现了两次!于是整个程序就不能通过编译了。你也许会认为这是程序员的失误——他应该知道b.h包含了a.h——但事实上他不应该知道。

使用#define配合条件编译可以很好地解决这个问题。在一个头文件中,通过#define定义一个名字,并且通过条件编译#ifndef...#endif使得编译器可以根据这个名字是否被定义,再决定要不要继续编译该头文中后续的内容。这个方法虽然简单,但是写头文件时一定记得写进去。

h文件和cpp文件实战

运行时include头文件会链接对应的cpp文件到调用h的cpp文件中。
.h文件中写出函数返回值类型、函数名、接受的参数,再到.cpp文件中去写函数的具体实现就行了
.cpp文件实现.h文件的声明

cpp文件实际上可以是任意include该头h文件的.cpp文件,但为了避免混乱,我们通常约定是在和头文件同名的.cpp文件中进行定义,可以认为是一种规范。

h文件不能用include,因为include的头文件会链接cpp文件到h文件中。
反之若是h文件未被cpp定义仅仅声明则可以被h文件include。
跨文件继承
可以include“xxx.h”头文件然后:继承xxx.h头文件对应的xxx.cpp类.

h文件

h:类声明,继承关系,方法声明,成员变量,常量声明,const,define变量声明以及方法注释等等

注释在头文件中

注意:#ifndef...#endif
h文件不能用include,因为include的头文件会链接cpp文件到h文件中。

#ifndef…#endif(如果没定义xxx那就用这个)

给每个头文件定制一个唯一标识符,相互之间不可冲突多次编译。
#ifndef…#endif属于预处理功能中三种(宏定义define ,文件包含include和条件编译)中的第三种—-条件编译

c++中的# ifndef 与#endif

#ifndef X标识符 //先测试x是否被宏定义过,如果被定义则返回假,如果没有被定义则返回真。
#define X标识符  
 	// 如果X没有被宏定义过,定义为X,并执行该语句
#endif   //终止if

在理论上来说可以是自由命名的,但每个头文件的这个“标识”都应该是唯一的。标识的命名规则一般是头文件名全大写,前面加下划线,并把文件名中的“.”也变成下划线,如:stdio.h

#ifndef _STDIO_H

#define _STDIO_H

......

#endif

该代码段的含义是:如果标识X没有被定义,则重新定义标识X,执行#define的语句,如果标识X已经被定义,则直接执行#endif下面的语句,跳过这种的内容。

#ifndef 和#endif必须一起使用,若丢失#endif,程序可能会报错。

#ifndef起到的作用是防止一个源文件两次包含一个头文件。假如你有一个c源文件,它包含了多个头文件:头文件A和头文件B,而头文件B又包含了头文件A,则最终的效果是,该源文件包含了两次头文件A。若在头文件A中定义了结构体或者类类型,编译时会报大量的重复定义错误。所以要用到#ifndef 与#endif,当第一次包含该头文件时,由于没有定义,条件为真,这样就会包含# ifndef与#endif之间的代码,,当第二次包含头文件,前面已经定义好了,条件为假,# ifndef与#endif之间的代码也不会再次包含,这样就避免重复定义了。
总结来说:
条件指示符#ifndef的最主要目的是防止头文件的重复包含和编译。条件编译当然也可以用条件语句来实现,
但是用条件语句将会对整个源程序进行编译,生成的目标代码程序很长,而采用条件编译,则根据条件只编译其中的程序段1或程序段2,生成的目标程序较短。如果条件选择的程序段很长,采用条件编译的方法是十分必要的。
———————————————— 版权声明:本文为优快云博主「Carol_小菜鸟」的原创文章,遵循CC 4.0
BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.youkuaiyun.com/weixin_42213622/article/details/120026444

#ifdef…#endif(如果定义了xxx那就用这个)

灵活使用#ifdef指示符,我们可以区隔一些与特定头文件、程序库和其他文件版本有关的代码(DEBUG)。
代码举例:新建define.cpp文件

#include "iostream.h"
int main()
{
#ifdef DEBUG      
cout<< "Beginning execution of main()";
#endif      
return 0;
}
运行结果为:Press any key to continue

改写代码如下:
#include "iostream.h"
#define DEBUG
int main()
{
#ifdef DEBUG      
cout<< "Beginning execution of main()";
#endif       
return 0;
}
运行结果为:Beginning execution of main()
                      Press any key to continue

更一般的情况是,#define语句是包含在一个特定的头文件中。
比如,新建头文件head.h,在文件中加入代码:

#ifndef DEBUG
#define DEBUG
#endif

而在define.cpp源文件中,代码修改如下:
#include "iostream.h"
#include "head.h" 
int main(){
#ifdef DEBUG      
cout<< "Beginning execution of main()";
#endif       
return 0;
}

C++:enum枚举数据类型(详细举例)

基本概念:

  • 枚举数据类型是一种由程序员定义的数据类型,其合法值是与它们关联的一组命名整数常量
  • 枚举类型(enumeration)是 C++ 中的一种派生数据类型,它是由用户定义的若干枚举常量的集合;
  • 枚举类型的定义格式为:

enum <数据类型名> {<枚举常量表>};
//关键字enum——指明其后的标识符是一个枚举类型的名字;

//枚举常量表——由枚举常量构成。“枚举常量"或称"枚举成员”,是以标识符形式表示的整型量,表示枚举类型的取值。各枚举常量之间以","间隔,且必须各不相同,且枚举变量一般是以大写字母表示(或首字母大写);

枚举常量的取值默认是从0开始,也可以通过给第一个常量赋值来决定从那个数值开始。
在这里插入图片描述
使用注意

  • 枚举变量可以直接输出,但不能直接输入。
  • 不能直接将常量赋给枚举变量。
  • 不同类型的枚举变量之间不能相互赋值。
  • 枚举变量的输入输出一般都采用switch语句将其转换为字符或字符串;枚举类型数据的其他处理也往往应用switch语句,以保证程序的合法性和可读性。

关键字

C++:typedef使用

typedef介绍
使用 typedef 定义新类型的方法(步骤):在传统的变量声明表达式里,用(新的)类型名替换变量名,然后把关键字 typedef 加在该语句的开头就可以了。

下面以两个示例,描述 typedef 的用法步骤。

示例1:

【第一步】:int a; ———— 传统变量声明表达式
【第二步】:int myint_t; ———— 使用新的类型名myint_t替换变量名a
【第三步】:typedef int myint_t; ———— 在语句开头加上typedef关键字,myint_t就是我们定义的新类型

示例2:

【第一步】:void (*pfunA)(int a); ———— 传统变量(函数)声明表达式
【第二步】:void (*PFUNA)(int a); ———— 使用新的类型名PFUNA替换变量名pfunA
【第三步】:typedef void (*PFUNA)(int a); ———— 在语句开头加上typedef关键字,PFUNA就是我们定义的新类型

特别强调:上述两个示例,仅仅为了演示 typedef 的用法步骤,便于新手记忆 typedef 的用法。在实际编写代码时,只有“第三步”对应的语句会写入代码中!

举例

test1和test2都是A类的对象

class A{
......
}
typedef  A  a;
A test1;
a test2;

C++:extern使用

C++中extern使用说明
C++中extern是指全局的意思。

extern 声明变量 为了被用来多文件共享同一变量。
文件件共享变量:首先要include包含相关头文件 源文件中定义 当前文件extern声明即可

extern置于变量前表示声明,提示编译器该变量定义在其他文件或类中。需要注意的是在声明变量时不要赋值,否则编译器会认为这不仅是声明,还是在定义的行为,会出现重复定义变量的错误。

  • extern int a = 200;是定义。
  • extern int a; 是声明;声明是没有变量空间,不可以赋值。
int a;//声明并定义变量a
int a = 1;//声明并定义变量a并赋值
extern int a;//声明变量a
extern int a = 1;//定义变量a并赋值

extern & const

extern 声明变量 为了被用来多文件共享同一变量

声明时赋值 等同于定义 就没有实际意义了

extern变量 表示该变量应该先去其他文件中搜索其定义 然后共享

声明:extern const A a; 
定义:const A a =。。。。。。 

文件件共享变量:首先要包含相关头文件 源文件中定义 当前文件extern声明即可

const 对象必须初始化 可用表达式 可普通变量初始化 使用过程中 编译器会找到const变量初始值做替换 所以const被限定为文件中有效
但要文多件共享也是可以的 方法:声明和定义都添加extern关键字 其他的同一般变量共享方式相同
在这里插入图片描述

  1. const对象是文件局部变量。意味着,其他文件将不能Link到本文件的Const变量。
  2. 如果在const前面加extern,意味着这个const对象可以被其他文件link到。
  3. 在通过extern const变量进行获取声明而非初始化的时候,编译器会首先选择寻找这句extern之前有没有该对象的初始化声明。这是容易理解的,因为const的作用域本来就是文件作用域。如果在自己extern之前找不到(编译器是自上而下编译代码,它并不知道自己之后是什么),那么去外部看有没有其他使用extern方式初始化这个对象的地方。如果有,编译通过。如果没有会报link错误。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值