当前位置:   article > 正文

C++基础知识_c++ csdn

c++ csdn

1、C 和 C++ 区别

  1. C++ 允许我们⾃⼰定义⾃⼰的命名空间,C 中不可以
  2. C++ 有新增的语法和关键字。动态管理内存方面,C++ 在 malloc 和 free 的基础上增加了 new 和 delete; C++ 中在指针的基础上增加了引⽤的概念;例如 C++中增加了 auto,explicit体现显示和隐式转换上的概念要求,还有 dynamic_cast 增加安全⽅⾯的内容。
  3. 函数⽅⾯ C++ 中有重载和虚函数的概念:C++ ⽀持函数重载⽽ C 不⽀持,是因为 C++ 函数的名字修饰与 C 不同,C++ 函数名字的修饰会将参数加在后⾯,例如,int func(int,double)经过名字修饰之后会变成_func_int_double,⽽C 中则会变成 _func,所以 C++ 中会⽀持不同参数调⽤不同函数。C++ 还有虚函数概念,⽤以实现多态。
  4. C 的 struct 和 C++ 的类也有很⼤不同:C++ 中的 struct 不仅可以有成员变量还可以成员函数,⽽且对于struct 增加了权限访问的概念,struct 的默认成员访问权限和默认继承权限都是public,C++ 中除了 struct 还有class 表示类,struct 和 class 还有⼀点不同在于 class的默认成员访问权限和默认继承权限都是 private。
  5. C 重点在于算法和数据结构。C 程序的设计⾸先考虑的是如何通过⼀个代码,⼀个过程对输⼊进⾏运算处理输出。 C++ ⾸先考虑的是如何构造⼀个对象模型,让这个模型能够契合与之对应的问题领域,这样就能通过获取对象的状态信息得到输出。
  6. C++ 中增加了模板还重⽤代码,提供了更加强⼤的 STL 标准库。

2、C++ 和 Java 区别

  1. 指针:Java 语⾔没有指针的概念,有内存的⾃动管理功能,让程序员没法找到指针来直接访问内存。但并⾮ Java 中没有指针,Java 虚拟机内部中还是⽤了指针Java 程序的安全。
  2. 多重继承:C++ ⽀持多重继承(一个派生类从多个基类继承而来), Java 不⽀持多重继承,但⽀持⼀个类继承多个接⼝,实现 C++ 中多重继承的功能,⼜避免了 C++ 的多重继承带来的不便。
  3. 数据类型和类:Java 是完全⾯向对象的语⾔,所有的函数和变量必须是类的⼀部分。除了基本数据类型之外,其余的都作为类对象,对象将数据和⽅法结合起来,把它们封装在类中,这样每个对象都可以实现⾃⼰的特点和⾏为。
  4. ⾃动内存管理:Java 程序中所有对象都是⽤ new 操作符建⽴在内存堆栈上,Java ⾃动进⾏⽆⽤内存回收操作,不需要程序员进⾏⼿动删除。⽽ C++ 中必须由程序员释放内存资源。Java 中当⼀个对象不再被⽤到时, ⽆⽤内存回收器将给他们加上标签。
  5. Java 不⽀持操作符重载。操作符重载被认为是 C++ 的突出特性。
  6. Java 不⽀持预处理功能。C++ 在编译过程中都有⼀个预编译阶段,Java 没有预处理器,但它提供了 import 与 C++预处理器具有类似功能。
  7. 类型转换:C++ 中有数据类型隐含转换的机制,Java 中需要限时强制类型转换。

3、C++ 中的四种智能指针

智能指针的作⽤是管理⼀个指针,避免程序员申请的空间,在函数结束时忘记释放,造成内存泄漏这种情况发⽣。因为智能指针就是⼀个类,当超出了类的作⽤域是,类会⾃动调⽤析构函数,析构函数会⾃动释放资源。

1. auto_ptr(C++98 的⽅案,C11 已抛弃)

auto_ptr<std::string> p1 (new string ("hello")); 
auto_ptr<std::string> p2;
p2 = p1; //auto_ptr 不会报错.
  • 1
  • 2
  • 3

此时不会报错,p2 剥夺了 p1 的所有权,但是当程序运⾏时访问 p1 将会报错。所以存在潜在的内存崩溃问题!

2. unique_ptr(替换 auto_ptr )
实现独占式拥有概念,保证同⼀时间内只有⼀个智能指针可以指向该对象。它对于避免资源泄露特别有⽤。

unique_ptr<string> p3 (new string (auto));
unique_ptr<string> p4;
p4 = p3;//此时会报错
  • 1
  • 2
  • 3

编译器认为 p4=p3 ⾮法,避免了 p3 不再指向有效数据的问题。

3. shared_ptr(共享型,强引⽤)
实现共享式拥有概念,多个智能指针可以指向相同对象,该对象和其相关资源会在“最后⼀个引⽤被销毁”时候释放。当我们调⽤ release() 时,当前指针会释放资源所有权,计数减⼀。当计数等于 0
时,资源会被释放。

4. weak_ptr(弱引⽤)
weak_ptr 是⼀种不控制对象⽣命周期的智能指针,它指向⼀个 shared_ptr 管理的对象。C++11标准虽然将 weak_ptr 定位为智能指针的一种,但该类型指针通常不单独使用。
当 weak_ptr 类型指针的指向和某一 shared_ptr 指针相同时,weak_ptr 指针并不会使所指堆内存的引用计数加 1;同样,当 weak_ptr 指针被释放时,之前所指堆内存的引用计数也不会因此而减 1。
weak_ptr 是⽤来解决 shared_ptr 相互引⽤时的死锁问题,如果说两个 shared_ptr 相互引⽤,那么这两个指针的引⽤计数永远不可能下降为0,也就是资源永远不会释放。解决办法:把其中⼀个改为weak_ptr就可以。

4、C++ 中内存分配情况

:由编译器管理分配和回收,存放局部变量和函数参数。
:需要⼿动 new malloc delete free 进⾏分配和回收,但可能会出现内存泄漏和空闲碎⽚的情况。
全局/静态存储区:存储初始化和未初始化的全局变量和静态变量。
常量存储区:存储常量,⼀般不允许修改。
代码区:存放程序的⼆进制代码。

5、指针和引用的区别

  1. 指针是一个变量,存储的是一个地址,指向内存的一个存储单元。
  2. 引用是原变量的一个别名,跟原来的变量实质上是同一个东西。
  3. 指针可以有多级,引用只能是一级。
  4. 指针可以指向NULL,引用不可以为NULL
  5. 指针可以在定义的时候不初始化,引用必须在定义的时候初始化。
int *p; // 合法
int &r; // 不合法
int a = 996;
int &r = a; // 合法
  • 1
  • 2
  • 3
  • 4
  1. 指针初始化之后可以再改变,引用不可以
int a = 996;
int *p = &a; // 初始化, p 是 a 的地址
int &r = a; // 初始化, r 是 a 的引用

int b = 885;
p = &b;	// 合法, p 更改为 b 的地址
r = b; 	// 不合法, r 不可以再变
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  1. sizeof 的运算结果不同,在64位机器上,int* 类型的大小为8个字节,int类型的大小为4个字节。
int a = 996;
int *p = &a;
int &r = a;

cout << sizeof(p); // 返回 int* 类型的大小
cout << sizeof(r); // 返回 int 类型的大小
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

9.作为函数参数时,传指针的实质是传值,传递的值是指针的地址
比如void test1(int* p) ,给*p赋值会导致实参也改变,但给p赋值,实参不会改变
传引⽤的实质是传地址,传递的是变量的地址,被调函数对形参的任何操作都会影响主调函数中的实参

#include <iostream>
using namespace std;

void test1(int* p, int& q) {
    *p = 200;
    q = 200;
}

void test2(int* p) {
    int a = 300;
    p = &a;
}

void main() {
    int num1 = 100;
    int num2 = 100;
    test1(&num1, num2);
    cout << num1 << endl;
    cout << num2 << endl << endl;
    test2(&num1);
    cout << num1 << endl;
}
// 输出结果为
200
200
200
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26

6、const VS static

static关键字详解
static 作⽤:控制变量的存储⽅式和可⻅性

  1. 修饰局部变量:编译器一般不对普通局部变量进行初始化,也就是说它的值在初始时是不确定的,除非对其显式赋值,静态局部变量使用static修饰符定义,即使在声明时未赋初值,编译器也会把它初始化为0。⼀般情况下,局部变量是存放在栈区的,并且局部的⽣命周期在包含语句块
    执⾏结束时便结束了。但是如果⽤ static
    关键字修饰的话,该变量便会存放在静态数据区,其⽣命周期会⼀直延续到整个程序执⾏结束。但其作⽤域并没有改变,作⽤域还是限制在其语句块。
  2. 修饰全部变量:对于⼀个全局变量,它既可以在本⽂件中被访问到,也可以在同⼀个⼯程中其它源⽂件被 访问(添加 extern进⾏声明即可)。⽤ static 对全局变量进⾏修饰后,仅在本⽂件可⻅。
  3. 修饰函数:⽤ static 修饰函数,情况和修饰全局变量类似。
  4. 修饰类:如果 对类中的某个函数或者变量⽤ static 修饰,则表示该函数或者变量属于⼀个类⽽不是属于此类的任何特定对象,无论定义了多少个类的对象,静态数据成员的拷贝只有一个,且对该类的所有对象可见。

const 关键字
用 const 定义的变量的值是不允许改变的,即不允许给它重新赋值,即使是赋相同的值也不可以。
const修饰指针和引用
如果 const 位于⼩星星的左侧,则 const 就是⽤来修饰指针所指向的变量;

int const *pi = &a; 
  • 1

如果 const 位于⼩星星的右侧,则 const 就是修饰指针本身。

int* const pi = &i;
  • 1

7、const VS define

  1. define是预编译指令,而const是普通变量的定义。
    define定义的宏是在预处理阶段展开的,而const定义的只读变量是在编译运行阶段使用的。
  2. const定义的是变量,而define定义的是常量。define定义的宏在编译后就不存在了,它不占用内存,因为它不是变量,系统只会给变量分配内存。但const定义的常变量本质上仍然是一个变量,用const定义的常变量具有宏的优点,而且使用更方便。所以编程时在使用const和define都可以的情况下尽量使用常变量来取代宏。
  3. 所以const定义的对象有数据类型,而宏定义的对象没有数据类型。所以编译器可以对前者进行类型安全检查,而对后者只是机械地进行字符替换,没有类型安全检查。

8、重载、重写、重定义

1.重载 :⼏个具有不同参数列表的同名函数,重载不关⼼函数的返回类型
2.重写:派⽣类中重新定义⽗类中除了函数体外完全相同的虚函数,返回值和形参都不能改变
3.重定义:派⽣类重新定义⽗类中相同名字的⾮虚函数,参数列表和返回类型都可以不同

9、C++ 所有的构造函数

  1. 无参构造函数:如果没有明确写出无参构造函数,会自动生成默认的无参构造函数
  2. 重载构造函数:普通构造函数可以有各种参数形式,一个类可以有多个普通构造函数,前提是参数的个数或者类型不同,创建对象时根据传入参数的不同调用不同的构造函数
  3. 拷贝构造函数:拷贝构造函数的函数参数为对象本身的引用,用于根据一个已经存在的对象复制出一个新的该类的对象,如果没有显式的写拷贝构造函数,那么系统默认会生成一个拷贝构造函数。但是当前类中有指针成员时,最好不要使用编译器提供的默认的拷贝构造函数,最好自己定义并在类中进行深拷贝
class test{
    int x;
    int y;
    test(test &another){
        x = another.x;
        y = anoyher.y;
    }
}
    // 调用拷贝构造函数
    test test2(test1);
    test test2 = test1;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  1. 变换构造函数(类型转换构造函数):只有一个参数,不允许默认转换则要将其声明为 explict 的,来阻⽌⼀些隐式转换的发⽣。
    test(int a) {
        x = a;
        y = 0;
    }
    // 调用变换构造函数
    test test1 = 100;
    test test2;
    test2 = 100;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

10、int float double 最大值,最小值

在这里插入图片描述

11、多重继承的二义性

看这里

通过在子类中指定继承的函数来自哪个父类:son.Parent_f::show();

12、内联函数

(1)在定义函数时添加inline关键字,在编译时,编译器会把该函数的代码副本放置在每个调用该函数的地方。(只在函数声明时添加inline关键字则无效)
(2)函数调用是有时间和空间开销的,如果函数体代码比较多,需要较长的执行时间,那么函数调用机制占用的时间可以忽略;如果函数只有一两条语句,那么大部分的时间都会花费在函数调用机制上
(3)如果内联函数的函数体非常大,那么编译后的程序体积也将会变得很大,一般只将那些短小的、频繁调用的函数声明为内联函数。
(4)对函数作 inline 声明不是强制性的,并非一经指定为 inline 编译器就必须这样做。编译器有自己的判断能力,它会根据具体情况决定是否这样做。
(5)内联函数不允许使用循环语句和开关语句(switch)

13、auto关键字&decltype关键字

1、auto : 根据变量的初始值推导出变量类型。
C++11中新增,在定义auto变量的时候就必须初始化
知道类型没有问题但⼜不想完整地写出类型的时候使用,最常用的地方如STL标准库中的迭代器:
在这里插入图片描述
2、decltype:根据表达式exp推导出变量name的类型,与初始化值value无关

decltype(exp) name = value;
decltype(exp) name
decltype(a) b = 1;  //b 被推导成了 int
  • 1
  • 2
  • 3

(1)如果 exp 是一个不被()包围的表达式,或一个单独的变量,那么 decltype(exp) 的类型就和 exp 一致
(2)如果 exp 是函数调用,那么 decltype(exp) 的类型就和函数返回值的类型一致。
(3)如果 exp 是一个左值,或者被括号( )包围,那么 decltype(exp) 的类型就是 exp 的引用

    int n = 0, m = 0;
    decltype(n + m) c = 0;  //n+m 得到一个右值,符合推导规则一,所以推导结果为 int 
    decltype(n = n + m) d = c;  //n=n+m 得到一个左值,符号推导规则三,所以推导结果为 int&
  • 1
  • 2
  • 3

14、volatile关键字

(1)volatile代表变量的易变性,变量的值可能随时被修改;
(2)volatile告诉编译器,不要对访问该变量的代码进行各种各样的优化;
(3)volatile修饰的变量每次必须从内存中读取值,不能从寄存器中读取。

15、有了malloc / free, 为什么还需要new / delete

(1)malloc与free是C/C++的标准库函数,new/delete是C++的运算符
(2)对象在创建的同时要自动执行构造函数,对象的消亡之前要自动执行析构函数
(3)malloc/free是库函数而不是运算符,不在编译器控制权限之内
(4)使用malloc/free并不会自动调用构造/析构函数

C++程序经常要调用C函数,而C程序只能用malloc/free管理动态内存
内部数据类型没有构造与析构,对它们而言malloc/free和new/delete是等价的

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/盐析白兔/article/detail/917519
推荐阅读
相关标签
  

闽ICP备14008679号