1.什么是类,什么是面向对象

(1)类是一种蓝图或者模板,用于定义对象的属性和行为,类通常包括:属性,也就是静态特征,方法,也就是动态特征。属性描述对象的特征,在初始化中输入或者直接写入,方法就是针对于类的函数
(2)而对象就是类的具体实例化,比如class Solution: sol=Solution() 类是定义对象的规则和结构,而对象是实际使用类的实例。类可以看作是“设计图”,而对象是根据设计图构建出的“实物”。
(3)面向对象编程是一种编程范式,使用对象来组织程序的逻辑。
(4)封装:属性,方法隐藏,对外隐藏细节,只提供公开的接口,
(5)继承:允许代码重用与拓展。
(6)多态:不同类的对象可以使用相同的方法名表现出不同的行为,在C++中,通过虚函数和函数重写实现多态,如果忘记在基类中将函数声明为虚函数,那你需要回到基类定义中,将该函数改为虚函数,以实现多态。而python由于其动态类型特征,所有的方法都是虚函数,天然支持多态。

2.在头文件中进行类的声明,在对应的实现文件中进行类的定义有什么意义

声明告诉编译器某个变量,函数,类的存在,但没有具体的实现,多个源文件可以共享相同的声明,提高代码的复用性和编译效率,编译一次生成对应的obj文件后再次用该类就不需要重复编译了。

3.内联函数

类内部函数自动为内联函数,内联函数的工作原理是在编译阶段进行函数替换,但是会造成代码膨胀

4.成员函数通过什么来区分不同对象的成员函数

他内部隐式地传递了一个指针,叫做 this 指针。这个指针指向的是当前调用成员函数的那个对象的内存地址。

5. C++编译器自动为类产生的四个缺省函数是什么?

(1)默认构造函数,拷贝构造函数,析构函数,赋值函数,他们用于管理对象的生命周期和内存分配
(2)默认构造函数:就是python中的属性,用于初始化对象,你定义了,他就有,你不定义,他就默认给你生成一个
(3)拷贝构造函数:就是说,你用一个旧的对象赋值给新的对象,拷贝构造函数就会调用,用于创建一个浅拷贝,在动态内存管理的时候,你得自己自定义一个拷贝构造函数用来深拷贝。
(4)析构函数:是对象生命周期结束的时候调用的特殊函数,释放对象占用的资源。类名前加~,没有参数也没有返回值
(5)赋值运算符函数:处理现有对象之间的赋值操作,确保正确的资源管理和拷贝

6.构造函数与普通函数相比在形式上有什么不同?

构造函数是类的特殊成员函数,是用于初始化的,他不具有任何类型也不返回任何值。

7.什么时候必须重写拷贝构造函数:

当拷贝构造函数涉及到动态存储分配空间时,要自己写拷贝构造函数,并且要深拷贝。

8.构造函数的调用顺序是:

调用基类构造函数->按声明顺序初始化数据成员->最后调用自己的构造函数

9.new,delete,malloc和free

delete和new对应,是运算符,不是函数,delete调用析构函数,new调用构造函数
malloc和free是标准库函数,不在编译器控制权限之内,所以无法满足动态对象的要求。

10.delete和delete[]

delete只会调用一次析构函数,但是delete[]会调用每一个成员的析构函数。

11.子类析构和父类析构的关系:

析构函数调用的次序是先派生类,后基类,定义一个对象的时候是先基类构造函数,再派生类构造函数,那么析构的时候刚好相反。

12.虚函数和纯虚函数

虚函数:在基类中冠以关键字virtual的成员函数,提供了一个接口界面,允许派生类中对基类的虚函数重新定义
纯虚函数:在基类中声明,以便在派生类根据需要进行定义

13.什么是引用

引用在 C++ 中是一种强大的工具,允许你使用另一个名称来操作同一个变量。主要特点包括:
(1)必须在声明时初始化。
(2)一旦绑定到某个变量,不能再绑定到其他变量。
(3)引用本身不占用额外的内存。
int a = 10;
int& ref = a;

14.引用的好处

(1)引用不会像按值传递一样创建副本,不用额外占用内存资源
(2)引用和指针效果类似,但是引用语法更简洁,安全。
引用和指针的区别:
(1)引用必须初始化,指针不必
(2)引用初始化后不可改变,指针不必
(3)不存在空值的引用,指针可空

15.常引用

如果既要利用引用提高程序的效率,又要保护传递给函数的数据不在函数中被改变,就应使用常引用。比如原参数是一个数,通过常引用使用这个变量的时候,就不可更改,使用本身的时候便可以更改,就是这个意思
(1)保护数据不被修改
(2)支持临时对象的传递
(3)提高效率

16.结构体struct和联合union

结构体可以给每一个变量一个存储空间,union不行,在同一时间只能保存一个成员的值。

17.重载和重写

重载(overload)发生在同一个类里的事情,因为输入输出的数据类型不一样,所以调用的时候,编译器会智能的区分不同的函数,也就是说,编译器认为函数名一样,但是输入类型不一样的前提下,这两个函数就是两个函数,所以就不会报错,但是你胆敢写两个同样的输入输出那完蛋了,报错重定义
重写(override)是两个类,子类和父类之间,父类有一个虚函数,子类重写这个函数,那么就可以做到多态,在实例化的时候,不同的对象根据他不同的类调用同一个函数,是多态的一种形式。

18.有哪几种情况只能用intialization list而不能用assignment(赋值)?

答案:当类中含有const、reference 成员变量;基类的构造函数都需要初始化表。

19.C++不是类型安全的

提供了一些类型转换机制,其中包括 reinterpret_cast,它允许将指针从一种类型转换为另一种完全不相关的类型,这在某些情况下可能导致不安全的操作。C#是的

20.main函数执行前执行的代码是:

全局对象的构造函数会在main函数之前运行

21.内存分配方式

(1)static:从静态存储区域分配,全局变量,和static变量都是静态的存储区域
(2)stack:在栈上创建,执行函数的时候,局部变量的存储单元在栈上创建,执行完则释放
(3)heap:从堆上分配,动态内存分配,程序在运行的时候通过malloc或者new来申请任意多的内存,有程序员来决定在哪里释放内存,动态内存的生命周期由程序员自己决定

22.const和#define

(1)const可以定义数据类型,define只是文本替换,也就是说之后的调用的时候,还是需要定义数据类型,定义的过程容易出现问题
(2)有一些集成的调试工具可以对const常量进行调试,但是不能对宏常量进行调试

23.数组和指针

数组:在内存中占用连续空间,大小在编译时确定,内容可修改(除非是常量数组)。
指针:指向任意内存位置,大小固定,指向的内存块的大小不容易确定,指向常量字符串时内容不可修改。

24.基类的析构函数不是虚函数,会带来什么问题?

派生类的析构函数用不上,会造成资源的泄漏。也就是说,别手贱的把基类默认的虚函数的析构函数给人家变成普通的成员函数了,这样派生类的析构函数就废了,然后析构的时候,基类释放了,派生类没能释放。

25.关于重写

重写:

如果派生类定义了一个与基类非虚成员函数同名、参数列表相同的函数,这个函数在派生类中被称为“隐藏”(hiding)基类的函数,而不是重写(override)。
这种情况下,基类的非虚函数不会被覆盖,而是隐藏。当你通过基类的指针或引用调用这个函数时,仍然会调用基类的版本。

class Base {
public:
    void func() { // 非虚函数
        std::cout << "Base func" << std::endl;
    }
};

class Derived : public Base {
public:
    void func() { // 这是隐藏,而不是重写
        std::cout << "Derived func" << std::endl;
    }
};

void example() {
    Base* b = new Derived();
    b->func(); // 调用的是 Base::func(),输出 "Base func"
    delete b;
}

Derived 类中的 func() 隐藏了 Base 类中的 func(),但并没有重写它。
通过基类指针 b 调用 func() 时,调用的是基类的实现,而不是派生类的实现。

26.全局变量和局部变量有什么区别?是怎么实现的?操作系统和编译器是怎么知道的?

参考上面的内存分配问题,全局变量存储在静态存储区域,而局部变量分配在栈区,随着局部函数的销毁而销毁

27.在c++中,不能像python一样进行函数的嵌套定义,这是不被允许的

(1)在 C++ 中,不能在一个函数内部定义另一个函数,但可以使用类成员函数或 Lambda 表达式。
(2)通过值传递,函数不能修改原始变量;通过引用或指针传递,可以允许函数修改原始变量。
(3)如果希望保持参数不变,使用值传递是合适的;如果希望修改,可以使用引用或指针。

28.C语言和C++的区别

C语言面向过程编程
C++面向对象编程
C++还有封装,继承,多态三大特性
C++还有许多类型安全的功能,比如强制类型转换,智能指针
C++支持泛型编程,库多,是山里灵活的狗

29.类的大小和什么有关

首先,类的大小遵循结构体的对齐原则
类的大小与普通数据成员有关,与成员函数和静态成员无关。即普通成员函数、静态成员函数、静态数据成员、静态常亮数据成员均对类的大小无影响。
虚函数对类的大小有影响,是因为虚函数表指针带来的影响
虚继承对类的大小有影响,是因为虚基表指针带来的影响

30.函数后面加const

const只能加在类的成员函数后面(普通函数不可以),也就是说这些成员函数是只读函数。

31.静态变量

第一次遇见的时候,初始化一次,之后即使它们的作用域在函数内部,每次调用该函数时也不会重新初始化,而是保留上次调用时的值。

void func() {
    static int count = 0; // 只初始化一次
    count++;
    std::cout << count << std::endl; // 每次调用 func() 时会递增
}

32.全局变量和静态全局变量

全局变量:在文件的任何位置都可以访问,作用域是整个文件(如果在多个文件中定义同名全局变量,可能会导致冲突)。
静态全局变量:在定义它的源文件中可见,不能在其他文件中访问。这是通过在变量前加上 static 关键字来实现的。
如果在头文件中定义了全局变量(不使用 static),那么在包含这个头文件的其他源文件中都能访问这个变量。但这可能会导致链接错误,特别是在多个文件中包含同一头文件时,因为同名变量会导致冲突。
如果在头文件中定义了全局静态变量,那么其他源文件无法访问这个变量,因为它的作用域被限制在定义它的源文件内。

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐