欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

C++学习笔记:extern “C”的用法及众多基础知识

程序员文章站 2022-05-24 17:50:14
extern “c”的用法:都是在c++编译时,让部分代码按照c编译器的解释来编译。 可以修饰一个函数, extern "c" int initial...

extern “c”的用法:都是在c++编译时,让部分代码按照c编译器的解释来编译。

可以修饰一个函数,

extern "c" int initializewinsockifnecessary();

也可以修饰一大段(通常是我们自己写的代码,c语言的):

#ifdef __cplusplus
extern "c" {
#endif

....

#ifdef __cplusplus
}
#endif

下面两种形式的区别?

#include 
#include "file.h"

答:前者是从标准库路径寻找和引用file.h(找不到也不去当前路径找,也可以写成一个绝对路径但不好),而后者是从当前工作路径搜寻并引用file.h。

const 有什么用途?

答:在c/c++中,(1)可以定义const常量,(2)修饰函数的返回值和形参;

这里说修饰函数返回值,场景是返回引用的时候,不希望调用方修改返回值。

在c++中,还可以修饰函数的定义体,定义类的const成员函数。被const修饰的东西受到强制保护,可以预防意外的变动,提高了程序的健壮性。

const和#define有什么区别?

都可以定义常量,但define是预编译时展开的。因此const在对变量进行类型检查以及调试时都更好用。

sizeof对于c++还有一些要注意:

一个空类占1个字节,单一继承的空类占1个字节,虚继承涉及到虚指针所以占4个字节

sizeof不计算static变量占得内存(应该是计算类的size时不计算类中的static)。

指针和引用的区别?

答:指针和引用都提供了间接操作对象的功能。

(1) 指针定义时可以不初始化,而引用在定义时就要初始化,和一个对象绑定,而且一经绑定,只要引用存在,就会一直保持和该对象的绑定;

(2) 赋值行为的差异:指针赋值是将指针重新指向另外一个对象,而引用赋值则是修改对象本身;

(3) 指针之间存在类型转换,而引用分const引用和非const应用,非const引用只能和同类型的对象绑定,const引用可以绑定到不同但相关类型的对象或者右值。

空指针和悬垂指针的区别?智能指针?

答:空指针是指被赋值为null的指针;指向动态分配对象的指针,在对象被释放后,将会产生悬垂指针。

空指针可以被多次delete,而悬垂指针再次删除时程序会变得非常不稳定;

使用空指针和悬垂指针都是非法的,而且有可能造成程序崩溃,如果指针是空指针,尽管同样是崩溃,但和悬垂指针相比是一种可预料的崩溃(如果指针被释放但没有赋值为null,则称作野指针)。

避免悬垂指针可利用智能指针,就是给对象加个引用计数,初始化的时候置1,最后一次解引用才释放。

c++中有malloc/free,为什么还有new/delete?

由于malloc/free是库函数而不是运算符,不在编译器控制之内,不能把执行构造函数和析构函数的任务强加给它,因此,要完成对象的初始化和析构过程,c++还需要new/delete。

面向对象技术的基本概念是什么,三个基本特征是什么?

答:基本特征:封装、继承、多态。

c++空类默认有哪些成员函数?

默认构造函数

析构函数

拷贝构造函数

赋值运算符(operator=)

取址运算符(operator&)(一对,一个非const的,一个const的)

当然,所有这些只有当被需要才会产生。比如你定义了一个类,但从来没定义过该类的对象,也没使用过该类型的函数参数,那么基本啥也不会产生。在比如你从来没有进行过该类型对象之间的赋值,那么operator=不会被产生。

17-27没看,本文最下面复制了一下。

main函数执行之前会执行什么?执行之后还能执行代码吗?

1.全局对象的构造函数会在main函数之前执行。

2.可以用atexit()注册一个或多个函数退出清理函数,它们按照注册时的反顺序,在exit()或return时被调用。(或者on_exit()但这个函数不建议用。)

#include 
int atexit(void (*function)(void)); //return 0 on success.

注意,fork子进程时,这些函数会被继承到子进程。而exec系列执行成功后,所有函数会被清空。

#include 
void exit(int status);

exit()将进程正常退出,并将(status & 0377)返回到父进程的wait(),status可以是exit_success或exit_failure。

exit()会按出栈顺序(反序)依次调用atexit()/on_exit()注册的函数。然后将所有打开的stdio流进行flush并关闭,通过tmpfile()创建的文件也被删除。那么,如果某个清理函数中调用_exit()或把自己kill结果退出了,后续清理函数以及刷stdio就不会执行了。

如果子进程exit()了,会发送一个sigchld信号给父进程,如果父进程设置了sa_nocldwait,那这个信号是否发送是未定义的。

如果子进程exit(),父进程在等待(wait()系列函数)子进程的状态,或者父进程设置了sa_nocldwait或者将sigchld的处理置为sig_ign,子进程都会立即退出。而如果父进程既没有wait,又没有设置忽略子进程退出,子进程就会变成僵尸进程(除了一个字节的exit status,什么都没有),等以后父进程wait的时候仍能拿到status。

这里也说下exit()和_exit()的区别,_exit()让程序“立即”退出,它不会调用上述atexit()/on_exit()注册的函数。它关闭自己打开的所有文件描述符,但是否flush stdio以及是否删除tmpfile创建的文件是与具体实现相关的(即没有明确规定)。

#include 
void _exit(int status);

当然,_exit()会关闭文件描述符,所以可能也会有些delay(例如还没写完就close),如果想达到“立即”的目的,可以在_exit()之前调用一下tcflush()可能会有帮助。

—–没试验start—-

static数据成员和static成员函数

答:static数据成员:

static数据成员独立于该类的任意对象而存在;每个static数据成员是与类关联的对象,并不与该类的对象相关联。static数据成员(const

static数据成员除外)必须在类定义体的外部定义。不像普通数据成员,static成员不是通过类的构造函数进行初始化(不能放在初始化列表中),而是应该在定义时进行初始化。

static成员函数:

static成员函数没有this形参,它可以直接访问所属类的static成员,不能直接使用非static成员。因为static成员不是任何对象的组成部分,所以static成员不能被声明为const。同时,static成员函数也不能被声明为虚函数。

多态类中的虚函数表是 compile-time,还是 run-time时建立的

答案:虚拟函数表是在编译期就建立了,各个虚拟函数这时被组织成了一个虚拟函数的入口地址的数组。而对象的隐藏成员–虚拟函数表指针是在运行期–也就是构造函数被调用时进行初始化的,这是实现多态的关键。

一个父类写了一个 virtual 函数,如果子类覆盖它的函数不加 virtual ,也能实现多态在子类的空间里,有没有父类的这个函数,或者父类的私有变量

答案:只要基类在定义成员函数时已经声明了virtual关键字,在派生类实现的时候覆盖该函数时,virtual关键字可加可不加,不影响多态的实现。子类的空间里有父类的所有变量(static除外)。

—–没试验end—-

内联函数在编译时是否做参数类型检查?

答:内联函数要做参数类型检查, 这是内联函数跟宏相比的优势。

有1,2,….一直到n的无序数组,求排序算法,并且要求时间复杂度为o(n),空间复杂度o(1),使用交换,而且一次只能交换两个数。

答:很明显,使用桶式排序,空间复用原数组的下标。

======================

17.继承层次中,为什么基类析构函数是虚函数?

答:编译器总是根据类型来调用类成员函数。但是一个派生类的指针可以安全地转化为一个基类的指针。这样删除一个基类的指针的时候,c++不管这个指针指向一个基类对象还是一个派生类的对象,调用的都是基类的析构函数而不是派生类的。如果你依赖于派生类的析构函数的代码来释放资源,而没有重载析构函数,那么会有资源泄漏。

18.为什么构造函数不能为虚函数?

答:虚函数采用一种虚调用的方法。需调用是一种可以在只有部分信息的情况下工作的机制。如果创建一个对象,则需要知道对象的准确类型,因此构造函数不能为虚函数。

19.如果虚函数是有效的,那为什么不把所有函数设为虚函数?

答:不行。首先,虚函数是有代价的,由于每个虚函数的对象都要维护一个虚函数表,因此在使用虚函数的时候都会产生一定的开销,这是没有必要的。

20.构造函数可以是内联函数

21.什么是多态?多态有什么作用?

答:多态就是将基类类型的指针或者引用指向派生类型的对象。多态通过虚函数机制实现。

多态的作用是接口重用。

22.重载和覆盖有什么区别?

答:虚函数是基类希望派生类重新定义的函数,派生类重新定义基类虚函数的做法叫做覆盖;

重载就在允许在相同作用域中存在多个同名的函数,这些函数的参数表不同。重载的概念不属于面向对象,编译器根据函数不同的形参表对同名函数的名称做修饰,然后这些同名函数就成了不同的函数。

重载的确定是在编译时确定,是静态的;虚函数则是在运行时动态确定。

23.公有继承、受保护继承、私有继承

答:

(1)公有继承时,派生类对象可以访问基类中的公有成员,派生类的成员函数可以访问基类中的公有和受保护成员;

(2)私有继承时,基类的成员只能被直接派生类的成员访问,无法再往下继承;

(3)受保护继承时,基类的成员也只被直接派生类的成员访问,无法再往下继承。

24.公有继承时基类受保护的成员,可以通过派生类对象访问但不能修改。

25.有哪几种情况只能用构造函数初始化列表而不能用赋值初始化?

答:const成员,引用成员

26.什么是虚指针?

答:虚指针或虚函数指针是虚函数的实现细节。带有虚函数的每一个对象都有一个虚指针指向该类的虚函数表。

27.c++如何阻止一个类被实例化?一般在什么时候将构造函数声明为private?

答:(1)将类定义为抽象基类或者将构造函数声明为private;

(2)不允许类外部创建类对象,只能在类内部创建对象