C++知识点总结(持续更新)

1.结构体:
1)概念:一个或多个 同类型或者不同类型的数据 的数据的集合
2)字节对齐: 8字节对齐
3)与类的区别:
结构上: 类中有构造函数和析构函数,类的成员变量默认为private
结构体中没有构造函数和析构函数,结构体的所有成员默认为public
存储: 类是 引用类型抽象的, 使用堆存储
结构体是 实值类型具体的, 使用栈存储
功能: 两者都可以声明构造函数,但结构体中 构造函数不能带参,类中无限制;
结构体不能声明析构函数,类无限制
结构体无法被继承,类可以被继承
结构体无法使用new ,类可以使用new初始化
4)如何选择结构还是类:   
a.  堆栈的空间有限,对于大量的逻辑的对象,创建类要比创建结构好一些
b.   结构表示如点、矩形和颜色这样的轻量对象,例如,如果声明一个含有 1000 个点对象的数组,则将为引用每个对象分配附加的内存。在此情况下,结构的成本较低。
c.  在表现抽象和多级别的对象层次时,类是最好的选择
d.  大多数情况下该类型只是一些数据时,结构时最佳的选择 
5)与C结构体区别:
C++中结构体可以有成员变量 也可以有函数,C中结构体不能有函数只能有数据类型 的数据
6)不定长结构体: 即 结构体中含有指针
struct Node {
int a;
char * b;
};
2.引用

 1)概念:  用来 给 变量起外号
 2) 何处使用  :
  类型标识符 &引用名  =  引用对象;
  example  :     
      int a = 10;
      int &b = a;

3)与指针的区别:
a. 引用是给对象起别名,并没有额外占用内存,初始化时不能为空。指针占用了额外的内存空间(32位下4字节,64位下8字节);
指针指向某块内存,所以初始化的时候可以不用指向任何对象,但初始化的时候必须进行判空,但引用不需要;
b. 引用 其指向的内存地址不能改变,但内容可以改变
指针 指向的内存地址可以改变, 内容也可以改变
4)优缺点:
优点: 节省空间,尤其是在传输数据量过大的时候,效率高,节省空间
缺点: 引用一旦绑定了变量后,就不能改变内存地址
5) 左值和 右值引用
int a = 10;
//左值:有确定的内存地址和内容,可以被赋值和多次使用
int &b = a;
//右值 :没有名字的临时变量,不能被赋值,只能在一条语句中出现,如: 临时变量
int &&c = 10;

3.Const
1)作用:
关键字 ,表示不能被修改的意思
2)不同位置的用法
//const 修饰变量
int a = 10;
const int * a = &b;
int const * a = &b;
//const 在* 左边时,表示a的内存地址可以修改,但是内容不能被修改
int * const a = &b;
//const 在*右边时,表示a的内存地址不能被修改,但是内容可以修改
const int * const a = &b;
//*左右两边都有const时,表示a的内存地址,内容 都不能被修改
//const修饰函数
const int getvalue() const
//const 在函数头表示 返回值不能修改; const在函数尾 表示函数尾可读函数,成员不能被修改
{
int a = 10;
return a;
}
3) const与define的区别
作用方式:#define 只是单纯的字符替换,不进行数据检查;
const 会进行数据检查
作用地点:#define 作用于预编译阶段;
const 作用于程序的编译和运行阶段
存储方式: #define 有若干个备份,存储在栈区
const 只有一个备份,存储在堆区
#define 不可调试,const可进行调试

4.指针

  1. 概念:
    本质: 指针就是地址,地址就是指针
    2)一般指针:
    即 变量的内存地址 = 指针
    int a = 10;
    int * p = &a;

3)函数指针
即 将函数的地址用指针来存储
适用场景: 函数回调
#include “stdafx.h”
#include
using namespace std;
void test()
{
cout << “Hello world” << endl;
}

int main()
{
//函数指针赋值, 最终两个都能输出 Hello World
void(*p)() = test;
p();
void(*p1)() = &test;
p1();
return 0;
}

4) 智能指针
概念:
是指RII智能指针是RAII(Resource Acquisition Is Initialization,资源获取即初始化)机制对普通指针进行一次封装。 使得智能指针行为像是一个指针,但本质是一个对象。
C++代码实现:
// ConsoleApplication1.cpp : 定义控制台应用程序的入口点。
//

#include “stdafx.h”
#include
#include
#include
using namespace std;

class Test
{
public:
Test()
{
count++;
cout << “Test()” << endl;
}
Test(const Test&t)
{

	count++;
	cout << "Test()" << endl;
}
~Test()
{
	cout << "~Test()" << endl;
}

int use_count() 
{
	return count;
}
void output() 
{
	cout << "~Test()" << endl;
}

private:
int count = 0;
};

template
class AutoPtr
{
public:
AutoPtr()
{

}

AutoPtr(T * ptr) 
{
	this->ptr = ptr;
}
AutoPtr(const AutoPtr& ap)
{

	this->ptr = ap->ptr;
} 
~AutoPtr() 
{
	if (ptr != nullptr)
	{
		delete ptr;
	}
}
AutoPtr& operator = (const AutoPtr& ap)
{
	ptr = ap;
}
T& operator* () 
{
	return *ptr;
}

T* get() {
	return ptr;
}
T* operator->() {
	return ptr;
}

private:
AutoPtr(const AutoPtr&) = delete;
AutoPtr& operator=(const AutoPtr&) = delete;
private:
T * ptr;
};

//智能指针的产生
int main1()
{
AutoPtr p(new Test);
p->output();

return 0;

}

//独占 智能指针
//最常见的使用场景,就是替代原始指针,为动态申请的资源提供异常安全保证。
int main2()
{
unique_ptr p1(new int);
*p1 = 10;
cout << *p1 << endl;

unique_ptr s1(new string);
*s1 = "Hello world";
cout << *s1 << endl;

cout << s1->size() << endl;
return 0;

}

//共享 智能指针
int main3()
{
shared_ptr p1(new int);
*p1 = 10;
shared_ptr p2 = p1;
shared_ptr p3;
p3 = p2;

cout << p1.use_count() << endl;
return 0;

}

int main()
{

AutoPtr p2(p1);
AutoPtr p1(new Test); 


AutoPtr p3;
p3 = p2;

cout << p1->use_count() << endl;
return 0;

}

int main()
{
//智能指针下不能暴露裸指针
int * p = new int();
shared_ptr p1 = shared_ptr §; //暴露了裸指针
shared_ptr p2 = shared_ptr (new int); //隐藏了裸指针

}
5) 指针与引用的区别:
指针 在接收参数时,会自动开辟一个内存空间,类似深拷贝,引用则不是。
指针是一个变量,存储的是对象的内存地址,引用是变量的别名。
指针可以为空,但引用不能为空;
指针指向的内存地址可以改变,对象内容也能改变,但引用不能改变指向,只能改变内容;
sizeof指针得到的是本指针的大小,sizeof引用得到的是所指向变量的大小
引用本质是一个指针,同样会占4字节内存;指针是具体变量,需要占用存储空间
在传送较大数据量时, 引用更为效率和节省空间
6)安全性
引用在创建的同时必须初始化,即引用到一个有效的对象;而指针在定义的时候不必初始化,可以在定义后面的任何地方重新赋值.
不存在NULL引用,引用必须与合法的存储单元关联;而指针则可以是NULL.
引用一旦被初始化为指向一个对象,它就不能被改变为另一个对象的引用;而指针在任何时候都可以改变为指向另一个对象.给引用赋值并不是改变它和原始对象的绑定关系.
引用的创建和销毁并不会调用类的拷贝构造函数
  语言层面,引用的用法和对象一样;在二进制层面,引用一般都是通过指针来实现的,只不过编译器帮我们完成了转换。不存在空引用,并且引用一旦被初始化为指向一个对象,它就不能被改变为另一个对象的引用,显得很安全。
 const 指针仍然存在空指针,并且有可能产生野指针. 总的来说:引用既具有指针的效率,又具有变量使用的方便性和直观性.

5数组
概念:是一个固定大小并可以存储相同类型数据的顺序集合
1)一般数组
:存放一般数据类型 变量的数组
int a[5] = {1,2,3,4,5};
2)指针数组
:存放指针的数组
int a[5] = { 1,2,3,4,5 };
int *p[5];
for (int i = 0; i < 5; ++i)
{
p[i] = &a[i];
}
for (int i = 0; i < 5; ++i)
{
cout << *(p[i]) << " ";
} a[5] = {1,2,3,4,5};
int *p [5];
for(int i = 0 ; i < 5; ++i)
{
p[i] = &a[i];
}

3)对象数组
:即提供了一个对象 ,然后存放在数组中,构成了对象数组
//创建对象A
class A
{
public:
int a;
int b;
};
int main()
{
//两种方式
// 声明一个A类型的数组temp 通过temp的下标 ,打点调用A中的成员
A temp[3];

temp[0].a = 1;
temp[0].b = 2;
cout << temp[0].a << endl;
cout << temp[0].b << endl;

//new新对象数组
A * temp1 = new A[3];
temp1[0].a = 1;
temp1[0].b = 2;
cout << temp1[0].a << endl;
cout << temp1[0].b << endl;
return 0;
}
4)结构体数组
: 即提供了一个结构体,然后声明存放在数组中 ,构成了结构体数组
//创建一个struct 结构体
struct Node
{
int a;
int b;
};
int main()
{

//创建一个结构体类型的数组进行存放数据
struct Node temp[3];
temp[0].a = 1;
temp[0].b = 2;
cout << temp[0].a << endl;
cout << temp[0].b << endl;

return 0;

}
5)二维,多维数组
:即 在原本一维数组的基础上增加维度 ,形成了二维甚至多维数组,本质还是多个一维数组的叠加
int main()
{
//二维数组

int arr[5][5];//如果是多维的话  依次往arr后面继续增加一维维度即可
arr[0][1] = 1;
arr[0][2] = 2;

arr[1][1] = 3;
arr[1][2] = 4;

return 0;

}

6)内存连续性
自动存储连续性:在函数中声明定义的变量(包括形参),在函数调用时初始化,在函数返回时销毁。
静态存储连续性:在程序的整个运行周期内一直存在。
动态存储连续性:用new运算符申请的内存将一直存在,直到手动的调用delete回收内存。
线程存储连续性:C++11标准引入的新的存储连续性,变量的生存周期即线程的运行周期。
7)遍历
int main()
{
int arr[5] = {5,3,2,4,1};

//下标遍历
for (int i = 0; i < 5;++i )
{
cout << arr[i] << " ";
}
cout << endl;
//基于范围的for循环遍历
for (auto i : arr)
{
cout << i << " ";
}

cout << endl;
//迭代器遍历 C++11后特有的
//使用begin() 和 end() 来获取遍历开始和结束的地址,使用迭代器后会在使用后自动回收
for (int * p = begin(arr);p != end(arr);++p)
{
cout << *p << " ";
}

return 0;
}

8) 与指针的区别
存储结构:
数组: 是内存大小固定,且类型相同的连续数据集合,通常存放了已知数据;存储的是数据的内容
指针: 内存大小不固定的动态数据结构,通常指向了匿名数据;存储的是 数据的内存地址
访问方式:
数组: 直接访问数据内容
指针: 通过内存地址进行间接访问

6链表
1)概念:
创建一个struct类型的内存,代表一个点;在这个点的内存中存放了数据和下一个点的内存地址,这就是单向链表;
如果是双向的话,则是还有另一个地址,用于存放上一个点的内存地址。
2)构建链表
struct Node {
int value;
struct Node * next;
};

int main()
{

int a = 10;

//创建一个头节点
struct Node * p = new struct Node;
p->next = nullptr;

//创建一个新的节点 用来存储a的数据
struct Node * p1 = new struct Node;
p1->value = a;
p1->next = nullptr;

//p的next  指向了p1的内存地址,形成了单向链表
p->next = p1;

return 0;

}

3)链表增删和遍历等操作

typedef struct _LinkList{
Elemdata data;
_LinkList *next;

}List,Node;

//1、单链表初始化
bool initlist(List *&L){

L = new List;
if(!L) return false;
L->next=NULL;
return true;

}

//2、单链表前插法
bool insert_front(List *&L,Node *node ){//为什么这里只用传一个一级指针,一级指针不是只是只能值得传入不能将值带出的吗

if(!L||!node)return false;

node->next = L->next;
L->next=node;
return true;

}

//3、单链表尾插法

bool insert_end(List *&L,Node *node){

if(!L||!node)return		false;

//找到最后一个结点
Node *last = L;

while(last->next){
	last=last->next;

}

node->next=NULL;
last->next=node;
return true;

}

//4、单链表任意位置插入
bool insert_pos(List *&L,int i,Elemdata &e){

if(!L) return false;

int j=0;
List *p = L;
Node *s;

//找到第i个结点的前一个元素
while(p&&j<(i-1)){
	p=p->next;
	j++;
}


if(!p->next||j!=(i-1)) return false;

//找到前一个元素
s = new Node;
s->data=e;

s->next = p->next;
p->next = s;
return true;

}

//5、查找结点为i的元素
bool getElemdata(List *L,int i,Elemdata &e){

if(!L)return false;

List *p = L;
int j = 0;

while(jnext;
	j++;
}

if(j!=i&&p) return false;

e=p->data;
return true;

}

//6、判断元素e是否存在
bool isExitElemdate(List *L,Elemdata &e){

if(!L)return false;

int j=0;
List *p=L->next;

while(p&&p->data!=e){
	
	p=p->next;
	j++;
	
}
if(!p)return false;

return true;

}

//遍历链表
void initPrint(List *&L){

if(!L)return;

List *p = L->next;

while(p){
	
	
	printf("%d\t",p->data);
	p=p->next;
}

printf("\n");

}

//删除某个位置的结点
bool List_delete(List *&L,int i){

if(!L) return false;

int j=0;

//找到要删除结点的前一个结点
List *p = L;

while(p->next&&j<(i-1)){
	p=p->next;
	j++;
}

if(!p->next||j!=i-1) return false;

Node *d,*q;

d = p;		//i的前一个结点
q = p->next;//要删除的结点

d->next = q->next;

delete q;

return true;

}

//销毁单链表
void List_destoyed(List *&L){

if(!L) return ;
List *p = L;

Node *q;


while(p){

	q = p;
	p=p->next;
	cout<<"销毁结点元素"<data<

}

  1. 循环链表
    : 即链表的最后一个节点的next 保存头节点的内存地址 ,则链表在循环到最后一个节点的时候,又自动跳回了头节点进行循环
    5)双向链表
    : 即 在原本的struct结构体内 多加入一个 指针 用来保存上一个节点的内存地址
    struct List{
    int value;
    struct List * next;
    struct List * last;
    };

*** C++四种 强制类型转换
static
动态
const

7.内存区域
1) 堆:
是向高地址扩展的数结构,是不连续的内存区域。 一般堆区由 程序员手动分配,和释放,如果没有释放,在整个程序结束时,会由系统自动回收。
在C++中 ,一般用 new来创建,delete释放
int * p = new int(10);
delete p;
//arrary
int * arr = new int[10];
delete arr[];
2) 栈区 :
是用户存放程序临时创建的局部变量,也就是说我们函数括弧“{}”中定义的变量(但不包括static声明的变量,static意味着在数据段中存放变量)。除此以外,在函数被调用时,其参数也会被压入发起调用的进程栈中,并且待到调用结束后,函数的返回值也会被存放回栈中。由于栈的先进先出特点,所以栈特别方便用来保存/恢复调用现场。从这个意义上讲,我们可以把堆栈看成一个寄存、交换临时数据的内存区
本质: 由编译器自动分配释放,存放函数的参数值,局部变量,进程栈的函数返回值等

//参数 parameter b
int func1(int b){
//局部变量

int a = 10;
return a;

}
int main() {

int c = func1(1);
char * p = "123";
//上述都是栈区存储

}
3)代码段:
通常是指用来存放程序执行代码的一块内存区域。这部分区域的大小在程序运行前就已经确定,并且内存区域通常属于只读, 某些架构也允许代码段为可写,即允许修改程序。在代码段中,也有可能包含一些只读的常数变量,例如字符串常量等
如上面的 代码都是代码段
4)全局/静态存储区:
(.bss 段和 .data 段):存放全局变量和静态变量,程序运行结束操作系统自动释放,在 C 语言中,未初始化的放在 .bss 段中,初始化的放在 .data 段中,C++ 中不再区分了。
int a; //a 此时是全局变量 但是未初始化, 所以存储在全局存储区
int a1 = 0; //a1 此时 也是全局变量,但是进行了初始化,所以a1是在静态存储区
int func1()
{
static int b = 0;// b就是静态局部变量 在静态存储区
++b;
return b;
}
int main() {
int c = func1();

}    

5)常量存储区:
即 存储常量的区域。存放的是常量,不允许修改,程序运行结束自动释放
int main(){
char * p = “123456ABV” ; //此时123456ABV就是常量 ,存储在常量存储区
char arr[] = “123”; // 此时123也是字符串常量 存储在常量存储区
}
6)堆和栈的区别:
申请方式不同:栈是系统自动分配,堆是程序员申请。
2、系统响应不同:
  栈:只要栈的剩余空间大于所申请的空间,系统就会为程序提供内存,否则栈溢出。 
  堆:系统收到申请空间的请求后,会遍历一个操作系统用于记录内存空闲地址的链表,当找到一个空间大于所申请空间的堆结点后,就会为该结点从记录内存空闲地址的链表中删除,并将该结点的内存分配给程序,然后在这块内存区域的首地址处记录分配的大小,这样我们在使用delete来释放内存的时候,delete才能正确地识别并删除该内存区域的所有变量。另外,我们申请的内存空间与堆结点的内存空间不一定相等,这是系统会自动将堆结点上多出来的那部分内存空间回收到空闲链表中。
 
3、空间大小不同:栈是一块连续的区域,大小一般是1~2M;堆是不连续的区域,空间很大,上限取决于有效的虚拟内存。
 
4、碎片问题:栈是后进先出的队列,内存是连续的,而堆则在多次的new和delete后会产生很多碎片。
 
5、生长方向:栈是向下,堆是向上。
 
6、分配方式:堆是动态分配,没有静态分配。栈是静态分配和动态分配,静态分配由编译器完成,例如局部变量的内存分配;动态分配则由alloca函数分配,不同于堆的手工释放,它的分配是完全由编译器自动释放。
 
7、分配效率:栈是系统的底层数据结构,由专门的寄存器存放栈的地址,专门指令执行压栈出栈,这就决定了栈的效率比较高。而堆是C++函数库提供的,机制复杂,效率低。

8.malloc 、free 和 new、delete的区别:
1、new/delete是C++的操作符,而malloc/free是C中的函数。
2、new做两件事,一是分配内存,二是调用类的构造函数;同样,delete会调用类的析构函数和释放内存。而malloc和free只是分配和释放内存。
3、new建立的是一个对象,而malloc分配的是一块内存;new建立的对象可以用成员函数访问,不要直接访问它的地址空间;malloc分配的是一块内存区域,用指针访问,可以在里面移动指针;new出来的指针是带有类型信息的,而malloc返回的是void指针。
4、new/delete是保留字,不需要头文件支持;malloc/free需要头文件库函数支持。

9.类 :
1)概念:
类:⼀种既能表⽰属性⼜能表⽰⾏为的复合数据类型。
注意:C++中的结构和类没有本质的区别,但为了与C语⾔中的结构区分,⼀般使⽤类。
对象:就是类这种数据类型的创建出的实例,相当于结构变量。

#include
#include
using namespace std;
class A{

  A();//无参构造
  A(int a1,int &b1,const string &c1) //含参构造
  {
      a = a1;
      b = b1;
      c = c1;      
  }
//  A():a(1),b(2),c("ABC");// 初始化构造列表
 
  A(const A &A1)  //拷贝构造函数
  {
      a = A1.a;
      b = A1.b;
      c = A1.c;      
  }
  ~A() = delete;//析构函数  加上 = delete 使该函数失效 
  {
      if(b != nullptr)
       delete b; 
       b = nullptr;//避免野指针                                                     
  }                                                                                                                                                                                                     
  virtual ~A()
  {
      cout << "class A 实现虚构";      
  };            

private:
int a;
int * b;
string c;

};

class B : public A{
B();
~B()
{
cout << " class 实现析构 " ;
}
};

int main()
{
//实例化的三种方式
//1隐式创建
A a1;
//2 显示创建
A a2 = A();
// 3 new创建
A a3 = new A();

}
显式调用 和 隐式调用 以及 explicit 显式构造函数关键字使用:

静态成员和静态方法:
静态成员是所有对象共享的,所以不能在静态方法里访问非静态的元素。
非静态方法可以访问类的静态成员,也可以访问类的非静态成员
静态属性初始化:
全局变量、文件域的静态变量和类的静态成员变量在main执行之前的静态初始化过程中分配内存并初始化;局部静态变量(一般为函数内的静态变量)在第一次使用时分配内存并初始化。这里的变量包含内置数据类型和自定义类型的对象。

class Pet
{
public:
Pet(std::string theName);
~Pet();
static int getCount();
protected:
std::string name;
private:
static int count; //count属于private成员,只有Pet类里面的方法能访问。因此编写了方法 static int getCount();
//且外部方法不能修改此值。只有上面的析构函数和构造函数能修改。

this:
类中的this 一般指 当前类对象指针
final:
关键字 ,表示无法 继承和在子类中重写;
class test final // c++ 11 中,final此时修饰类 表示当前类test 无法被继承
{
public func1() final ; // 此时final 修饰方法 表示当前的函数方法 在子类中无法被重写
};
10 继承:
1)概念:
继承(inheritance)机制是面向对象程序设计使代码可以复用的最重要的手段,它允许程序员在保持原有类特性的基础上进行扩展,增加功能,这样产生新的类,称派生类。继承呈现了面 向对象程序设计的层次结构,体现了由简单到复杂的认知过程。以前我们接触的复用都是函数复用,继承是类设计层次的复用。
其他实现事例:

// ConsoleApplication1.cpp : 定义控制台应用程序的入口点。
//

#include “stdafx.h”
#include
#include “ConsoleApplication1.h”
using namespace std;

class A
{
public:
A() :a(0) {}
~A(){}

friend ostream& operator<< (ostream& out,const A& other) 
{
	out << other.a;
	return out;
}

//重载:函数重载是指在同一作用域内,可以有一组相同函数名,不同参数列表的函数,这组函数被称为重载函数。
int test(int x) 
{
	a = x;
	return a;
}
int test() 
{
	a = 100;
	return a;
}


//虚函数
// 	概念:
// 		在类中声明为 virtual的成员函数,称为虚函数。虚函数是C++中用于实现多态的机制。核心理念就是通过基类访问派生类定义的函数。是C++中多态性的一个重要体现,利用基类指针或基类对象的引用访问派生类中的成员函数,这种情况下使用虚函数。
// 		语法:
// 		class 类名
// 	{
// 	访问权限:
// 		virtual 类型 函数名(形参列表)
// 		{
// 			函数体
// 		}
// 	};
virtual int getres() 
{
	a = 20;
	return a;
}

virtual int changeres() 
{
	return -2;
}

protected:
int a;
};

class B : public A
//继承方式有三种:
// public(父类中所有成员访问权限不变)
// protected(父类的所有成员访问权限降一级)
// private(父类的所有成员都变为私有,只能自己使用)
{
public:
B(int x)
{
a = x;
}

//	重写(覆盖)
//		分别位置基类和派生类中,函数名相同,参数相同,基类函数必须是虚函数。

// 	virtual int getnum01()
// 	{
// 		return a = 50;
// 	}
virtual int getres() 
{
	a = 50;
	return  a;
}



// 	重定义(隐藏)
// 		派生类的函数屏蔽了与其同名的基类函数,规则如下:
// 		如果派生类的函数和基类的函数同名,但是参数不同,此时,不管是否虚函数,基类的函数被隐藏;
// 		如果派生类的函数与基类的函数同名,并且参数也相同,但是基类函数不是虚函数,此时,基类的函数被隐藏。
virtual int changeres() 
{
	return 11;
}

};

int main()
{
//显式实例化
A a2 = A();
//隐式实例化
A a;
cout << a.test() << endl;
cout << a.test(2) << endl;

//new对象实例化
A * a1 = new B(15);
cout << *a1 << endl;
cout << a1->getres() << endl;
cout << a1->changeres() << endl;
return 0;

}

2)纯虚函数:

//纯虚函数
// 概念:
// 纯虚函数是在基类中只声明函数,没有实现,要求在派生类中定义的成员函数。
// 语法:
// class 类名
// {
// 访问权限:
// virtual 类型 函数名(形参列表) = 0; //无函数体
// };

//

#include “stdafx.h”
#include
using namespace std;
class A
{
public:
virtual int getres() = 0;//纯虚函数 无函数体
protected:
int a;
};

class B : public A
{
public:
virtual int getres() //在派生类中实现函数
{
return 11;
}
};

int main()
{
A * a = new B;
cout << a->getres() << endl;
return 0;
}
3)虚继承:

当遇到“菱形继承”时,基类A的成员就会在派生类D中出现多份,造成访问基类A的成员不明确的问题。

// ConsoleApplication1.cpp : 定义控制台应用程序的入口点。
//

#include “stdafx.h”
#include
using namespace std;
class A
{
public:
int getres() {
return 11;
};
protected:
int a;
};

//在继承类时 加上关键字 virtual , 虚继承
class B : virtual public A
{
public:

};

class C : virtual public A
{
public:

};
class D : public B, public C //此时 D同时继承B,C 形成菱形继承,但是B,C 都虚继承了A 所以此时还是调用的A的方法实现,与B,C无关
{
public:
};

int main()
{
D d1;
cout << d1.getres() << endl;
return 0;
}

你可能感兴趣的:(C++,c++,开发语言)