C++ 编程基础总结

简介: C++ 编程基础总结

STL 库描述

STL 库包括:容器、算法以及融合两者的迭代器。

容器分为顺序容器和关联容器。顺序容器比如 vector 是一个动态分配存储空间的容器。区别于 C++ 中的 arrayarray 分配的空间是静态的,分配之后不能被改变,而 vector 会自动重分配(扩展)空间。

内存中堆和栈的区别

  • 栈内存:由编译器自动分配释放,存放函数的参数值,局部变量的值等。其操作方式类似于数据结构中的栈,都是先进后出。栈使用的是一级缓存,他们通常都是被调用时处于存储空间中,调用完毕立即释放**。
  • 堆内存:一般由程序员分配释放,若程序员不释放,程序结束时可能由 OS 回收。堆是存放在二级缓存中,生命周期由虚拟机的垃圾回收算法来决定(并不是一旦成为孤儿对象就能被回收)。所以调用这些对象的速度要相对来得低一些。

堆栈溢出一般是由什么原因导致的?

没有垃圾回收资源。

sizeof 的作用

sizeof 运算符返回一条表达式或一个类型名字所占的字节数,其满足右结合规律。

C++ 面向对象特点

数据抽象、继承和动态绑定。

多态的理解

  • 同一函数作用于不同的对象,会对应不同的实现,从而产生不同的执行结果。在运行时,可以通过指向基类的指针或引用,来调用实现派生类中的方法。
  • C++ 的多态性具体体现在运行和编译两个方面:在程序运行时的多态性通过继承和虚函数来体现;在程序编译时多态性体现在函数和运算符的重载上;

虚函数的理解

Python 不同,在 C++ 中,基类将类型相关的函数与派生类不做改变直接继承的函数区分对待。对于某些函数,基类希望它的派生类各自定义适合自身的版本,此使基类就会将这些函数声明成虚函数。同时,派生类内部必须在其内部对所有重新定义的虚函数进行声明。(参考 C++ Primer p526 页)

派生类必须使用类派生列表(class derivation list)明确指出它是从哪个(哪些)基类继承而来。派生的形式是:首先一个冒号,后面紧跟以逗号分隔的基类列表,其中每个基类前面可以有访问说明符。

class Student: public People{
    string names;
    virtual  double get_gpa (vector<float> scores);
}
复制代码

动态绑定的理解

在 C++ 语言中,,当我们使用基类的引用(或指针)调用一个虚函数时,将发生动态绑定,即函数运行的版本由实参决定,运行时自动选择函数的版本。

C++构造函数初始化时什么时候只能用初始化列表?

如果类成员是 const、引用。或者属于某种未提供默认构造函数的类类型时,我们必须通过构造函数初始值列表为这些成员提供初始值。(参考 C++ Primer p259)

C++ 构造函数和析构函数的初始化顺序

本回答参考C++ 构造函数初始化顺序, C++奇奇怪怪的题目之构造析构顺序

多个基类的派生类(多继承) 的构造函数初始化按照如下顺序进行:

  1. 先执行虚拟继承的父类的构造函数;
  2. 然后从左到右执行普通继承的父类的构造函数;
  3. 接着按照定义的顺序执行数据成员的初始化;
  4. 最后调用类自身的构造函数;

析构函数就无脑的将构造函数顺序反转即可。多继承形式下的构造函数和单继承形式基本相同,只是要在派生类的构造函数中调用多个基类的构造函数。

实例代码如下:

#include <iostream>
using namespace std;
class OBJ1
{
public:
    OBJ1() { cout << "OBJ1" << endl; }
    ~OBJ1() { cout << "OBJ1 destory" << endl;}
};
class OBJ2
{
public:
    OBJ2() { cout << "OBJ2\n"; }
    ~OBJ2(){cout << "OBJ2 destory" <<endl;}
};
class Base1
{
public:
    Base1() { cout << "Base1" << endl; }
    ~Base1() { cout << "Base1 destory" << endl; }
};
class Base2
{
public:
    Base2() { cout << "Base2" << endl; }
    ~Base2() { cout << "Base2 destory" << endl; }
};
class Base3
{
public:
    Base3() { cout << "Base3" << endl; }
    ~Base3() { cout << "Base3 destory" << endl; }
};
class Base4
{
public:
    Base4() { cout << "Base4" << endl; }
    ~Base4() { cout << "Base4 destory" << endl; }
};
class Derived :public Base1, virtual public Base2,
    public Base3, virtual public Base4
{
public:
    Derived() { cout << "Derived ok" << endl; }
    ~Derived() { cout << "Derived destory" << endl; }
protected:
    OBJ1 obj1;
    OBJ2 obj2;
};
int main()
{
    Derived aa;
    cout << "construct ok"<<endl;
    return 0;
}
复制代码

程序输出结果如下:

Base2 Base4 Base1 Base3 OBJ1 OBJ2 Derived ok construct ok Derived destory OBJ2 destory OBJ1 destory Base3 destory Base1 destory Base4 destory Base2 destory

全局变量和局部变量在内存中是否有区别?如果有,是什么区别?

全局变量储存在静态数据区,局部变量在堆栈中。

C++ 中的 new delete 和 C 语言中的 malloc free 有什么区别

虽然这两者都分别是完成分配内存和释放内存的功能,但是 C++new 分配内存时会调用构造函数,用 delete 释放内存时会调用析构函数。

new、delete、malloc、free 区别

  • newdeleteC++ 的运算符,mallocfree 是 C++/C 语言的标准框函数,都可用于申请动态内存和释放内存。
  • new 动过调用对象的构造函数来申请动态内存;delete 通过调用对象的析构函数来释放内存。
  • 对于非内部数据类型的对象而言,只用 maloc/free 是无法满足动态对象的要求。我们知道对象在创建的同时要自动执行构造函数,对象在消亡之前要自动执行析构函数。但由于 malloc/free 是库函数而不是运算符,不在编译器控制权限之内,不能够把执行构造函数和析构函数的任务强加于 malloc/free。因此 new/delete 其实比 malloc/free 更灵活。

static 关键字作用

  • 声明全局静态变量:在全局变量前加上关键字 static,全局变量就定义成一个全局静态变量,作用域在声明它的文件之外是不可见的,即从定义之处开始到文件结尾
  • 局部静态变量:在局部变量前加上关键字 static,作用域仍然为局部作用域,即当定义它的函数或者语句块结束的时候,作用域结束。
  • 静态函数: 在函数返回类型前加 static,静态函数只在声明他的文件中可见,不能被其他文件使用。
  • 类的静态成员:在类中,静态成员可以实现多个对象之间的数据共享,即静态成员是类的所有对象中共享的成员,而不是某个对象成员。并且使用静态数据成员不会破坏隐藏的原则,保证了数据的安全性。
  • 类的静态函数:把函数成员声明为静态的,就可以把函数与类的任何特定对象独立开来。静态成员函数即使在类对象不存在的情况下也能被调用,静态函数只要使用类名加范围解析运算符 :: 就可以访问(<类名>::<静态成员函数名>(<参数表>))

类的 static 变量在什么时候初始化?函数的 static 变量在什么时候初始化?

类的静态成员变量在类实例化之前就已经存在了,并且分配了内存。函数的static 变量在执行此函数时进行初始化。

C++ 变量作用域

作用域即是程序的一个区域,在程序中变量的作用域一般有三个地方:

  • 在函数或者一个代码块内部声明的变量,称为局部变量;
  • 在函数参数中定义的变量,称为形参;
  • 在所有函数外部声明的变量,比如在程序文件开头定义的变量,称为全局变量。

C++ 指针和引用的区别

  • 指针有自己的内存空间,而引用只是一个别名,类似于Python浅拷贝和深拷贝的区别
  • 不存在空引用, 引用必须链接到一块合法的内存地址;
  • 一旦引用被初始化为一个对象,就不能指向另一个对象。指针可以在任何时候指向任何一个对象;
  • 引用必须在创建时被初始化。指针可以在任何时间初始化。
  • 指针可以有多级,但是引用只能是一级(int **p;合法 而 int &&a 是不合法的)。

C++ 中析构函数的作用

析构函数与构造函数对应,类的析构函数是类的一种特殊的成员函数,它会在每次删除所创建的对象时执行。析构函数的名称与类的名称是完全相同的,只是在前面加了个波浪号(~)作为前缀,它不会返回任何值,也不能带有任何参数。析构函数有助于在跳出程序(比如关闭文件、释放内存等)前释放资源。

C++ 静态函数和虚函数的区别

静态函数在编译的时候就已经确定运行时机,虚函数在运行的时候动态绑定。虚函数因为用了虚函数表机制,调用的时候会增加一次内存开销。

++i 和 i++ 区别

++i 先自增1,再返回,i++,先返回 i,再自增1.

const 关键字作用

const类型的对象在程序执行期间不能被修改改变。

C++如何传递数组给函数

首先要知道的是,C++ 传数组给一个函数,该数组类型会自动转换为指针,因此实际传递的是地址。

一维数组作为形参有以下三种方式,多维数组作为形参类似。

  • 形参是一个指针;
  • 形参是已定义大小的数组;
  • 形参是未定义大小的数组。


相关文章
|
15天前
|
算法 编译器 C语言
探索C++编程的奥秘与魅力
探索C++编程的奥秘与魅力
|
2月前
|
安全 算法 C++
【C/C++ 泛型编程 应用篇】C++ 如何通过Type traits处理弱枚举和强枚举
【C/C++ 泛型编程 应用篇】C++ 如何通过Type traits处理弱枚举和强枚举
51 3
|
2月前
|
算法 编译器 C++
【C/C++ 泛型编程 应用篇】C++ 如何通过Type traits 判断 Lambda表达式类型?
【C/C++ 泛型编程 应用篇】C++ 如何通过Type traits 判断 Lambda表达式类型?
43 4
|
15天前
|
编译器 C语言 C++
C语言,C++编程软件比较(推荐的编程软件)
C语言,C++编程软件比较(推荐的编程软件)
|
2月前
|
安全 程序员 编译器
【C/C++ 泛型编程 进阶篇 Type traits 】C++类型特征探究:编译时类型判断的艺术
【C/C++ 泛型编程 进阶篇 Type traits 】C++类型特征探究:编译时类型判断的艺术
185 1
|
2月前
|
算法 程序员 C++
【C/C++ 泛型编程 应用篇】C++ 对多参数的参数包的 参数类型提取 应用
【C/C++ 泛型编程 应用篇】C++ 对多参数的参数包的 参数类型提取 应用
44 5
|
2月前
|
存储 缓存 算法
高效编程:我们应该了解哪些编译器优化技术?如何做出成熟的优化行为,掌握C++编程中的编译器优化艺术。
高效编程:我们应该了解哪些编译器优化技术?如何做出成熟的优化行为,掌握C++编程中的编译器优化艺术。
99 4
|
1天前
|
算法 安全 编译器
【C++】从零开始认识泛型编程 — 模版
泛型编程是C++中十分关键的一环,泛型编程是C++编程中的一项强大功能,它通过模板提供了类型无关的代码,使得C++程序可以更加灵活和高效,极大的简便了我们编写代码的工作量。
12 3
|
2天前
|
存储 算法 编译器
C++的模板与泛型编程探秘
C++的模板与泛型编程探秘
7 0
|
11天前
|
算法 程序员 C语言
C++:深度探索与编程实践
C++:深度探索与编程实践
15 3