yhwang-hub
还是什么也不写吧
展开
-
C++中的 =default 和 =delete
编译器默认为一个类生成的默认函数默认构造函数默认析构函数默认拷贝构造函数默认赋值函数移动构造函数移动拷贝函数class DataOnly {public: DataOnly () // default constructor ~DataOnly () // destructor DataOnly (const DataOnly & rhs) // copy con原创 2021-05-23 11:09:55 · 750 阅读 · 0 评论 -
单链表和双链表的删除和插入的时间复杂度分析
1.单向链表要删除某一节点时,必须要先通过遍历的方式找到前驱节点(开发中大致分为两种删除方式:1.通过待删除节点序号2.按值查找)。若仅仅知道待删除节点,是不能知道前驱节点的,故单链表的增删操作复杂度为O(n)。2.双链表(双向链表)知道要删除某一节点p时,获取其前驱节点q的方式为 q = p->prior,不必再进行遍历。故时间复杂度为O(1)。而若只知道待删除节点的序号,则依然要按序查找,时间复杂度仍为O(n)。3.单、双链表的插入操作,若给定前驱节点,则时间复杂度均为O(1)。否则只能按序或原创 2020-09-26 15:03:10 · 8049 阅读 · 0 评论 -
什么是红黑树?
什么是红黑树?二叉查找树(BST)具备什么特性呢?1.左子树上所有结点的值均小于或等于它的根结点的值。2.右子树上所有结点的值均大于或等于它的根结点的值。3.左、右子树也分别为二叉排序树。下图中这棵树,就是一颗典型的二叉查找树:1.查看根节点9:2.由于10 > 9,因此查看右孩子13:3.由于10 < 13,因此查看左孩子11:4.由于10 < 11,因此查看左孩子10,发现10正是要查找的节点:假设初始的二叉查找树只原创 2020-09-25 14:17:51 · 157 阅读 · 1 评论 -
红黑树的查找时间复杂度O(logn)
红黑树查找时间复杂度如果二叉排序树是平衡的,则n个节点的二叉排序树的高度为Log2n+1,其查找效率为O(Log2n),近似于折半查找。如果二叉排序树完全不平衡,则其深度可达到n,查找效率为O(n),退化为顺序查找。一般的,二叉排序树的查找性能在O(Log2n)到O(n)之间。因此,为了获得较好的查找性能,就要构造一棵平衡的二叉排序树。红黑树并不是一个完美平衡二叉查找树,根结点的左子树如果比右子树高,但左子树和右子树的黑结点的层数是相等的,也即任意一个结点到到每个叶子结点的路径都包含数量相同的黑结点。所原创 2020-09-25 14:07:58 · 18688 阅读 · 0 评论 -
c++ vector的底层实现
简单来说就是,每个动态数组都分配有一定容量,当存储的数据达到容量的上限的时候,就重新分配内存。我觉得最重要的就是resize这个函数:void resize(int st){ //重新分配空间,在堆区新开辟内存,然后将以前数组的值赋给他,删除以前的数组 int *newData = new int[st]; for (int i = 0; i < _size; i++) { newData[i] = data[i]; } delet原创 2020-09-25 13:57:29 · 378 阅读 · 0 评论 -
什么情况下,类的析构函数应该声明为虚函数?为什么?
基类的指针指向bai派生类对象,并用基类的指针删du除派生类对象时。虚析构函zhi数是为了解决这dao样的一个问题:基类的指针指向派生类对象,并用基类的指针删除派生类对象。如果某个类不包含虚函数,那一般是表示它将不作为一个基类来使用。当一个类不准备作为基类使用时,使析构函数为虚一般是个坏主意。因为它会为类增加一个虚函数表,使得对象的体积翻倍,还有可能降低其可移植性。所以基本的一条是:无故的声明虚析构函数和永远不去声明一样是错误的。实际上,很多人这样总结:当且仅当类里包含至少一个虚函数的时候才去声明虚析原创 2020-09-25 13:43:16 · 598 阅读 · 0 评论 -
C++的三种单例模式-----深度解析
简介因为在设计或开发中,肯定会有这么一种情况,一个类只能有一个对象被创建,如果有多个对象的话,可能会导致状态的混乱和不一致。这种情况下,单例模式是最恰当的解决办法。它有很多种实现方式,各自的特性不相同,使用的情形也不相同。今天要实现的是常用的三种,分别是饿汉式、懒汉式和多线程式。通过单例模式, 可以做到:确保一个类只有一个实例被建立提供了一个对对象的全局访问指针在不影响单例类的客户端的情况下允许将来有多个实例懒汉式懒汉式的特点是延迟加载,比如配置文件,采用懒汉式的方法,顾名思义,懒汉么,很原创 2020-09-25 12:42:02 · 2376 阅读 · 0 评论 -
为什么每个线程都需要创建一个栈?
有四个函数A、B、C、D,地址分别为100、200、300、400;有两个线程同时执行;1)假如只有一个栈函数A在线程1中执行的时候,调用了函数B,将函数A中下一条指令的地址入栈(104),然后执行函数B;函数B中又执行了Yield()函数(蓝色,Yield()的作用可以理解为切换线程),Yield()切换到地址300处的线程,执行线程2,同时将下一条指令的地址入栈(204);接下来执行函数C,同样道理调用方法D,304入栈;最后执行函数D,Yield() 会跳到地址204继续执行204;紧接原创 2020-09-24 23:12:51 · 586 阅读 · 0 评论 -
多线程和多进程的区别
然后我们来看下现场称和进程间的比较1)需要频繁创建销毁的优先用线程。实例:web服务器。来一个建立一个线程,断了就销毁线程。要是用进程,创建和销毁的代价是很难承受的。2)需要进行大量计算的优先使用线程。所谓大量计算,当然就是要消耗很多cpu,切换频繁了,这种情况先线程是最合适的。实例:图像处理、算法处理3)强相关的处理用线程,若相关的处理用进程。什么叫强相关、弱相关?理论上很难定义,给个简单的例子就明白了。一般的server需要完成如下任务:消息收发和消息处理。消息收发和消息处理就是.原创 2020-09-24 19:56:32 · 155 阅读 · 0 评论 -
数组索引的kdtree建立及简明快速的k近邻搜索方法
数组索引的kdtree建立及简明快速的k近邻搜索方法1. kdtree概念2. 建立kdtree3. k近邻搜索1. kdtree概念kd树(k-dimensional树的简称),是一种分割k维数据空间的数据结构,主要应用于多维空间关键数据的搜索,如范围搜索和最近邻搜索。如下图所示,在既定的分割维度上,每一个根节点的值均大于其左子树,并小于其右子树。这样的二叉树,对于搜索某个点的最临近点或k近邻点,是十分高效快速的。2. 建立kdtree建立kdtree,主要有两步操作:选择合适的分割维度,选择原创 2020-09-14 23:37:00 · 394 阅读 · 0 评论 -
如何访问静态成员
声明为static的类成员能在类的范围内共享,这样的类成员就是类的静态成员。在类中,静态成员可以实现多个对象之间的数据共享,并且使用静态数据成员还不会破坏隐藏的原则,因此保证了安全性。因此,静态成员是类的所有对象中共享的成员,而不是某个对象的成员。使用静态数据成员可以节省内存,因为它是所有对象所共有的。因此,对多个对象来说,静态数据成员只会在内存中开辟一块存储空间,供所有对象使用,静态成员在类加载的时候就存在于内存中。静态数据成员的值对每个对象都是一样,但它的值是可以更新的。只要对静态数据成员的值更新一次原创 2020-09-09 16:29:17 · 2621 阅读 · 1 评论 -
C++ static_cast、dynamic_cast、const_cast和reinterpret_cast(四种类型转换运算符)
上节讲到,隐式类型转换是安全的,显式类型转换是有风险的,C语言之所以增加强制类型转换的语法,就是为了强调风险,让程序员意识到自己在做什么。但是,这种强调风险的方式还是比较粗放,粒度比较大,它并没有表明存在什么风险,风险程度如何。再者,C风格的强制类型转换统一使用( ),而( )在代码中随处可见,所以也不利于使用文本检索工具(例如 Windows 下的 Ctrl+F、Linux 下的 grep 命令、Mac 下的 Command+F)定位关键代码。为了使潜在风险更加细化,使问题追溯更加方便,使书写格式更加原创 2020-09-07 10:29:12 · 192 阅读 · 0 评论 -
C/C++类型转换的本质
在 C/C++ 中,不同的数据类型之间可以相互转换:无需用户指明如何转换的称为自动类型转换(隐式类型转换),需要用户显式地指明如何转换的称为强制类型转换(显式类型转换),这点已在《C++转换构造函数》中进行了说明。隐式类型转换利用的是编译器内置的转换规则,或者用户自定义的转换构造函数以及类型转换函数(这些都可以认为是已知的转换规则),例如从 int 到 double、从派生类到基类、从type *到void *、从 double 到 Complex 等。type *是一个具体类型的指针,例如int *、原创 2020-09-06 23:41:45 · 848 阅读 · 0 评论 -
C++类型转换函数:将当前类的类型转换为其它类型
转换构造函数能够将其它类型转换为当前类类型(例如将 double 类型转换为 Complex 类型),但是不能反过来将当前类类型转换为其它类型(例如将 Complex 类型转换为 double 类型)。C++ 提供了类型转换函数(Type conversion function)来解决这个问题。类型转换函数的作用就是将当前类类型转换为其它类型,它只能以成员函数的形式出现,也就是只能出现在类中。类型转换函数的语法格式为:operator type(){ //TODO: return da原创 2020-09-06 23:37:40 · 312 阅读 · 0 评论 -
C++拷贝控制操作
当定义一个类时,我们显式地或隐式地指定了此类型的对象在拷贝、赋值和销毁时做什么。一个类通过定义三种特殊的成员函数来控制这些操作,分别是拷贝构造函数、赋值运算符和析构函数。拷贝构造函数定义了当用同类型的另一个对象初始化新对象时做什么,赋值运算符定义了将一个对象赋予同类型的另一个对象时做什么,析构函数定义了此类型的对象销毁时做什么。我们将这些操作称为拷贝控制操作。由于拷贝控制操作是由三个特殊的成员函数来完成的,所以我们称此为“C++三法则”。在较新的 C++11 标准中,为了支持移动语义,又增加了移动构造函原创 2020-09-06 23:16:00 · 203 阅读 · 0 评论 -
C++深拷贝和浅拷贝(深复制和浅复制)
对于基本类型的数据以及简单的对象,它们之间的拷贝非常简单,就是按位复制内存。例如:class Base{public: Base(): m_a(0), m_b(0){ } Base(int a, int b): m_a(a), m_b(b){ }private: int m_a; int m_b;};int main(){ int a = 10; int b = a; //拷贝 Base obj1(10, 20); Base obj原创 2020-09-06 23:05:59 · 136 阅读 · 0 评论 -
到底什么时候会调用拷贝构造函数?
当以拷贝的方式初始化对象时会调用拷贝构造函数。这里有两个关键点,分别是「以拷贝的方式」和「初始化对象」。初始化对象初始化对象是指,为对象分配内存后第一次向内存中填充数据,这个过程会调用构造函数。对象被创建后必须立即被初始化,换句话说,只要创建对象,就会调用构造函数。初始化和赋值的区别初始化和赋值都是将数据写入内存中,并且从表面上看起来,初始化在很多时候都是以赋值的方式来实现的,所以很容易混淆。请看下面的例子:int a = 100; //以赋值的方式初始化a = 200; //赋值a =原创 2020-09-06 22:56:25 · 1423 阅读 · 0 评论 -
C++拷贝构造函数(复制构造函数)详解
拷贝和复制是一个意思,对应的英文单词都是copy。**对于计算机来说,拷贝是指用一份原有的、已经存在的数据创建出一份新的数据,最终的结果是多了一份相同的数据。**例如,将 Word 文档拷贝到U盘去复印店打印,将 D 盘的图片拷贝到桌面以方便浏览,将重要的文件上传到百度网盘以防止丢失等,都是「创建一份新数据」的意思。在 C++ 中,拷贝并没有脱离它本来的含义,只是将这个含义进行了“特化”,是指用已经存在的对象创建出一个新的对象。从本质上讲,对象也是一份数据,因为它会占用内存。严格来说,对象的创建包括两个原创 2020-09-06 22:32:27 · 289 阅读 · 0 评论 -
C++ exception类:C++标准异常的基类
C++语言本身或者标准库抛出的异常都是 exception 的子类,称为标准异常(Standard Exception)。你可以通过下面的语句来捕获所有的标准异常:try{ //可能抛出异常的语句}catch(exception &e){ //处理异常的语句}之所以使用引用,是为了提高效率。如果不使用引用,就要经历一次对象拷贝(要调用拷贝构造函数)的过程。exception 类位于 头文件中,它被声明为:class exception{public: exc原创 2020-09-06 22:13:09 · 494 阅读 · 0 评论 -
C++异常处理入门,C++ try catch入门
开发程序是一项“烧脑”的工作,程序员不但要经过长期的知识学习和思维训练,还要做到一丝不苟,注意每一个细节和边界。即使这样,也不能防止程序出错。专家指出,长期作息不规律 + 用脑过度的危害很大,可能会诱发神经衰弱、失眠等疾病。我就是受害者之一,曾被失眠困扰了好几年,不但入睡困难,还容易早醒。程序员要注意劳逸结合,多去健身房,多跑步,多打球,多陪女朋友旅游等,千万不要熬夜,以为深夜写代码效率高,这样会透支年轻的身体。程序的错误大致可以分为三种,分别是语法错误、逻辑错误和运行时错误:语法错误在编译和链接原创 2020-09-06 18:42:48 · 130 阅读 · 0 评论 -
C++类模板中的静态成员
类模板中可以定义静态成员,从该类模板实例化得到的所有类都包含同样的静态成员。程序示例如下:#include <iostream>using namespace std;template <class T>class A{private: static int count;public: A() { count ++; } ~A() { count -- ; }; A(A &) { count ++ ; } static原创 2020-09-06 17:31:20 · 324 阅读 · 0 评论 -
C++类模板与继承详解
目录1.类模板从类模板派生2. 类模板从模板类派生3. 类模板从普通类派生4. 普通类从模板类派生类模板和类模板之间、类模板和类之间可以互相继承。它们之间的派生关系有以下四种情况。1.类模板从类模板派生示例程序:template <class T1, class T2>class A{ Tl v1; T2 v2;};template <class T1, class T2>class B : public A <T2, T1>{ T1原创 2020-09-06 17:29:46 · 1114 阅读 · 1 评论 -
C++模板的显式具体化
C++ 没有办法限制类型参数的范围,我们可以使用任意一种类型来实例化模板。但是模板中的语句(函数体或者类体)不一定就能适应所有的类型,可能会有个别的类型没有意义,或者会导致语法错误。例如有下面的函数模板,它用来获取两个变量中较大的一个:template<class T> const T& Max(const T& a, const T& b){ return a > b ? a : b;}请读者注意a > b这条语句,>能够用来比较 i原创 2020-09-05 21:12:03 · 404 阅读 · 0 评论 -
C++函数模板的实参推断
在使用类模板创建对象时,程序员需要显式的指明实参(也就是具体的类型)。例如对于下面的 Point 类:template<typename T1, typename T2> class Point;我们可以在栈上创建对象,也可以在堆上创建对象:Point<int, int> p1(10, 20); //在栈上创建对象Point<char*, char*> *p = new Point<char*, char*>("东京180度", "北纬210度")原创 2020-09-05 14:53:48 · 733 阅读 · 0 评论 -
C++函数模板的重载
当需要对不同的类型使用同一种算法(同一个函数体)时,为了避免定义多个功能重复的函数,可以使用模板。然而,并非所有的类型都使用同一种算法,有些特定的类型需要单独处理,为了满足这种需求,C++ 允许对函数模板进行重载,程序员可以像重载常规函数那样重载模板定义。在《C++函数模板》一节中我们定义了 Swap() 函数用来交换两个变量的值,一种方案是使用指针,另外一种方案是使用引用,请看下面的代码://方案①:使用指针template<typename T> void Swap(T *a, T *原创 2020-09-05 14:39:37 · 723 阅读 · 0 评论 -
C++虚继承时的构造函数
在虚继承中,虚基类是由最终的派生类初始化的,换句话说,最终派生类的构造函数必须要调用虚基类的构造函数。对最终的派生类来说,虚基类是间接基类,而不是直接基类。这跟普通继承不同,在普通继承中,派生类构造函数中只能调用直接基类的构造函数,不能调用间接基类的。下面我们以菱形继承为例来演示构造函数的调用:#include <iostream>using namespace std;//虚基类Aclass A{public: A(int a);protected: int m原创 2020-09-05 11:17:58 · 457 阅读 · 0 评论 -
C++基类和派生类的析构函数
和构造函数类似,析构函数也不能被继承。与构造函数不同的是,在派生类的析构函数中不用显式地调用基类的析构函数,因为每个类只有一个析构函数,编译器知道如何选择,无需程序员干涉。另外析构函数的执行顺序和构造函数的执行顺序也刚好相反:创建派生类对象时,构造函数的执行顺序和继承顺序相同,即先执行基类构造函数,再执行派生类构造函数。而销毁派生类对象时,析构函数的执行顺序和继承顺序相反,即先执行派生类析构函数,再执行基类析构函数。#include <iostream>using namespace原创 2020-09-05 11:09:38 · 393 阅读 · 0 评论 -
C++基类和派生类的构造函数
前面我们说基类的成员函数可以被继承,可以通过派生类的对象访问,但这仅仅指的是普通的成员函数,类的构造函数不能被继承。构造函数不能被继承是有道理的,因为即使继承了,它的名字和派生类的名字也不一样,不能成为派生类的构造函数,当然更不能成为普通的成员函数。在设计派生类时,对继承过来的成员变量的初始化工作也要由派生类的构造函数完成,但是大部分基类都有 private 属性的成员变量,它们在派生类中无法访问,更不能使用派生类的构造函数来初始化。这种矛盾在C++继承中是普遍存在的,解决这个问题的思路是:在派生类的构原创 2020-09-05 10:52:33 · 305 阅读 · 0 评论 -
C++静态绑定和动态绑定,彻底理解多态
C/C++ 用变量来存储数据,用函数来定义一段可以重复使用的代码,它们最终都要放到内存中才能供 CPU 使用。CPU 通过地址来取得内存中的代码和数据,程序在执行过程中会告知 CPU 要执行的代码以及要读写的数据的地址。CPU 访问内存时需要的是地址,而不是变量名和函数名!变量名和函数名只是地址的一种助记符,当源文件被编译和链接成可执行程序后,它们都会被替换成地址。编译和链接过程的一项重要任务就是找到这些名称所对应的地址。假设变量 a、b、c 在内存中的地址分别是 0X1000、0X2000、0X300原创 2020-09-04 23:47:11 · 1806 阅读 · 0 评论 -
C++纯虚函数和抽象类详解
在C++中,可以将虚函数声明为纯虚函数,语法格式为:virtual 返回值类型 函数名 (函数参数) = 0;纯虚函数没有函数体,只有函数声明,在虚函数声明的结尾加上=0,表明此函数为纯虚函数。最后的=0并不表示函数返回值为0,它只起形式上的作用,告诉编译系统“这是纯虚函数”。包含纯虚函数的类称为抽象类(Abstract Class)。之所以说它抽象,是因为它无法实例化,也就是无法创建对象。原因很明显,纯虚函数没有函数体,不是完整的函数,无法调用,也无法为其分配内存空间。抽象类通常是作为基类,让原创 2020-09-04 23:43:47 · 499 阅读 · 0 评论 -
C++虚析构函数的必要性
上节我们讲到,构造函数不能是虚函数,因为派生类不能继承基类的构造函数,将构造函数声明为虚函数没有意义。这是原因之一,另外还有一个原因:C++ 中的构造函数用于在创建对象时进行初始化工作,在执行构造函数之前对象尚未创建完成,虚函数表尚不存在,也没有指向虚函数表的指针,所以此时无法查询虚函数表,也就不知道要调用哪一个构造函数。下节将会讲解虚函数表的概念。析构函数用于在销毁对象时进行清理工作,可以声明为虚函数,而且有时候必须要声明为虚函数。为了说明虚析构函数的必要性,请大家先看下面一个例子:#includ原创 2020-09-04 23:35:43 · 528 阅读 · 0 评论 -
C++虚函数注意事项以及构成多态的条件
C++ 虚函数对于多态具有决定性的作用,有虚函数才能构成多态。上节《C++多态和虚函数快速入门教程》我们已经介绍了虚函数的概念,这节我们来重点说一下虚函数的注意事项。只需要在虚函数的声明处加上 virtual 关键字,函数定义处可以加也可以不加。为了方便,你可以只将基类中的函数声明为虚函数,这样所有派生类中具有遮蔽关系的同名函数都将自动成为虚函数。关于名字遮蔽已在《C++继承时的名字遮蔽》一节中进行了讲解。当在基类中定义了虚函数时,如果派生类没有定义新的函数来遮蔽此函数,那么将使用基类的虚原创 2020-09-04 23:19:15 · 172 阅读 · 0 评论 -
C++多态和虚函数
在《C++将派生类赋值给基类(向上转型)》一节中讲到,基类的指针也可以指向派生类对象,请看下面的例子:#include <iostream>using namespace std;//基类Peopleclass People{public: People(char *name, int age); void display();protected: char *m_name; int m_age;};People::People(char *na原创 2020-09-04 22:37:03 · 133 阅读 · 0 评论 -
C++结构体使用sort函数进行排序
需要添加头文件语法描述:sort(begin,end,cmp)#include<iostream>#include<algorithm>#include<cstring>using namespace std;int main(){ int arr[5] = { 1,3,4,2,5 }; sort(arr, arr + 5); for (int i = 0; i < 5; ++i)cout << arr[i] <&原创 2020-08-21 23:32:56 · 705 阅读 · 0 评论 -
C++哈希表的原理和使用
目录1、概念2、散列表的构造方法2.1、直接定址法1、概念散列技术是在记录的存储位置和它的关键字之间建立一个确定的对应关系f,每个关键字key对应一个存储位置f(key),查找时,根据这个对应的关系找到给定值key的映射f(key),若查找集合中存在这个记录,则必定在f(key)的位置上。我们把这种对应关系f称为散列函数,又称为哈希(Hash)函数。采用散列技术将记录存储在一块存储空间中,这块连续空间称为散列表或哈希表(Hash-Table)。2、散列表的构造方法2.1、直接定址法直接定址法使用下转载 2020-08-18 14:48:02 · 5082 阅读 · 0 评论 -
C++运算符重载(operator)
所谓重载,就是赋予新的含义。函数重载(Function Overloading)可以让一个函数名有多种功能,在不同情况下进行不同的操作。运算符重载(Operator Overloading)也是一个道理,同一个运算符可以有不同的功能。实际上,我们已经在不知不觉中使用了运算符重载。例如,+号可以对不同类型(int、float 等)的数据进行加法操作;<<既是位移运算符,又可以配合 cout 向控制台输出数据。C++ 本身已经对这些运算符进行了重载。C++ 也允许程序员自己重载运算符,这给我们带原创 2020-08-17 23:26:41 · 286 阅读 · 0 评论 -
C++输入流和输出流
本教程一开始就提到,C++ 又可以称为“带类的 C”,即可以理解为 C++ 是 C 语言的基础上增加了面向对象(类和对象)。在此基础上,学过 C 语言的读者应该知道,它有一整套完成数据读写(I/O)的解决方案:1.使用 scanf()、gets() 等函数从键盘读取数据,使用 printf()、puts() 等函数向屏幕上输出数据;2.使用 fscanf()、fgets() 等函数读取文件中的数据,使用 fprintf()、fputs() 等函数向文件中写入数据。要知道,C 语言的这套 I/O 解决方原创 2020-08-17 16:49:37 · 228 阅读 · 0 评论 -
C++左值和右值
引用是一个变量,它引用其他变量的内存位置。例如,来看以下代码:int x = 34;int &lRef = x;在该代码中,标识符IRef就是一个引用。在声明中,引用是通过&符号来指定的,它出现在类型与变量的标识符之间,这种类型的引用成为左值引用。可以将左值看作是一个关联了名称的内存位置,允许程序的其他部分来访问它。在这里,我们将“名称”解释为任何可用于访问内存位置的表达式。所以,如果arr是一个数组,那么arr[1]和*(arr+1)都将被视为相同内存位置的“名称“。相对而言,右原创 2020-08-17 16:03:57 · 105 阅读 · 0 评论 -
C++指针和引用及区别
1.变量首先最重要的,variable的定义,当你申明一个变量的时候,计算机会将指定的一块内存空间和变量名进行绑定;这个定义很简单,但其实很抽象,例如:int x = 5; 这是一句最简单的变量赋值语句了, 将5赋值于名字叫做x的内存空间,其本质是将值5赋值到一块内存空间,而这个内存空间名叫做x。变量在内存中的操作其实是需要经过2个步骤的:1.找出与变量名相对应的内存地址。2.根据找到的地址,取出该地址对应的内存空间里面的值进行操作。2.指针指针的特殊之处在于:指针变量相对应的内存空间存储的值原创 2020-08-16 11:09:08 · 124 阅读 · 0 评论 -
C++虚继承和虚基类详解
多继承(Multiple Inheritance)是指从多个直接基类中产生派生类的能力,多继承的派生类继承了所有父类的成员。尽管概念上非常简单,但是多个基类的相互交织可能会带来错综复杂的设计问题,命名冲突就是不可回避的一个。多继承时很容易产生命名冲突,即使我们很小心地将所有类中的成员变量和成员函数都命名为不同的名字,命名冲突依然有可能发生,比如典型的是菱形继承,如下图所示:类 A 派生出类 B 和类 C,类 D 继承自类 B 和类 C,这个时候类 A 中的成员变量和成员函数继承到类 D 中变成了两份,原创 2020-08-16 10:04:01 · 248 阅读 · 0 评论