在前两章节中,我们学习了类的创建,封装,继承,构造函数和析构函数,其中很多都提到了权限,由于我们是从C语言过渡过来的,所以当时没有过多讲解,这一章节我们来讲解一下权限的控制和权限的继承。
一.类的创建规范
之前章节在创建类的时候,我们通常将构造函数定义在类成员中,由于这样可读性较差,所以我们需要将构造函数,析构函数的定义写在类中,将函数的实现定义在外部:
案例:
struct Base{
int a;
int b;
Base();
Base(int a,int b);
!Base();
};
Base::Base(int a,int b){
this->a = a;
this->b = b;
}
**需要注意的是,在类外部写函数实现的时候,需要在前面加上类名和::,告诉编译器这是类中的函数,否则编译器不认识这到底是类中的函数还是你定义的新函数。
二.public和private
我们在上一章节链接: C++ 类,this指针中讲解过权限控制符的具体介绍,不了解的可以去看一下,由于protected不经常使用,所以我们这里只介绍public和private。
我们来看看这样一段程序:
#include <stdio.h>
struct Base{
public:
int a;
int b;
private:
int c;
int d;
};
void Test(){
Base t;
t.a=1;
t.b=2;
t.c=3;
t.d=4;
}
int main(){
Test();
return 0;
}
在编译的时候,我们可以看到出错误了,具体内容为:cannot access private member declared in class 'Base'
告诉我们不能访问Base类中的成员c和d。
那么这就很好地体现了C++中的权限控制:
1.对外提供的函数或变量,发布成public的,但是后续不能随意更改。
2.可能会变动的函数或者变量,定义为private,这样在使用的时候编译器会自动检测。
3.只有结构体内部的成员(构造函数,析构函数和变量)可以访问private成员。
4.public/private可以修饰函数,也可以修饰变量。
三.public成员真的不能访问吗
在上面的程序中我们明显看到是不能访问的,但是对于学习底层的我们来说,我们还是可以访问的,我们可以通过指针来访问:
比如说这段代码:
#include <stdio.h>
struct Base{
public:
int a;
int b;
Base(int a,int b,int c,int d){
this->a = a;
this->b = b;
}
private:
int c;
int d;
};
void Test(){
Base t(1,2,3,4);
int* p;
p = (int*)&t;
printf("%d\n",*p);
printf("%d\n",*(p+1));
}
int main(){
Test();
return 0;
}
我们能看到函数输出窗口还是输出了a和b的值。所以说private的成员还是能访问的,但是我们要通过强制转化指针来访问。
四.struct和class的区别
我们知道struct是C语言中定义结构体的关键字,calss是C++中创建类的关键字,那么他俩到底有什么区别呢?我们通过一个例子来观察:
#include <stdio.h>
struct Base{
int a;
int b;
int c;
int d;
};
void Test(){
Base t;
t.a = 1;
t.b = 2;
t.c = 3;
t.d = 4;
}
int main(){
Test();
return 0;
}
我们先来通过struct关键字来定义,编译发现没有错误,那么我们再来通过class来创建一个类来观察一下:
#include <stdio.h>
class Base{
int a;
int b;
int c;
int d;
};
void Test(){
Base t;
t.a = 1;
t.b = 2;
t.c = 3;
t.d = 4;
}
int main(){
Test();
return 0;
}
这时候我们编译,发现出现了问题:编译器报错:cannot access private member declared in class 'Base'
并且报错四次,告诉我们不能访问Base中的成员a,b,c,d。而这样的报错,我们在前文中简介权限控制的时候就讲解过了。
那么我们就可以发现问题:我们用struct定义结构体的时候,它的成员权限默认为public,而我们calss创建类的时候,它的成员默认权限为private这就是struct和callss的区别。
五.private是否被继承
前文中我们提到:只有类中的成员才可以访问private变量或者函数,那么在继承的时候,private变量是否会继承呢?
我们首先通过观察类所占内存来分析是否继承:
#include <stdio.h>
class Base{
private:
int a;
int b;
public:
int c;
int d;
};
class Sub:public Base{
int e;
int f;
};
void Test(){
Sub t;
printf("%d",sizeof(Sub));
}
int main(){
Test();
return 0;
}
这里需要注意,我们继承的时候要在父类名前面加上public告诉编译器是共有类型,否则继承的父类变量全为private类型
我们在程序输出窗口可以看到输出了24
,说明Base类中不管是public类型还是private类型,都被继承过来了。
但是需要注意一点,继承过来的private类型,在子类中,无法使用,Base类中的private类型,只有Base类中的成员才可以调用
我们通过实例来论证:
#include <stdio.h>
class Base{
private:
int a;
int b;
public:
int c;
int d;
};
class Sub:public Base{
public:
int e;
int f;
};
void Test(){
Sub t;
t.a = 1;
t.b = 2;
t.c = 3;
t.d = 4;
t.e = 5;
t.f = 6;
}
int main(){
Test();
return 0;
}
我们编译时,编译器错误提示:
说明继承过来的Base类中的a和b,不能在子类中使用。
六.类的继承中的构造函数
我们再来观察一个有趣的过程:
#include <stdio.h>
class Base{
private:
int a;
int b;
public:
int c;
int d;
Base(){
printf("Base构造函数\n");
}
};
class Sub:public Base{
public:
int e;
int f;
Sub(int c,int d,int e,int f){
printf("Sub构造函数\n");
this->c = 1;
this->d = 2;
this->e = 3;
this->f = 4;
}
};
void Test(){
Sub t(1,2,3,4);
}
int main(){
Test();
return 0;
}
我们来到反汇编查看:
push 4
push 3
push 2
push 1
lea ecx,[ebp-18h]
call @ILT+60(Sub::Sub) (00401041)
我们可以看到Sub的构造函数又调用了一个构造函数,我们跟进去看一下:
mov dword ptr [ebp-4],ecx
mov ecx,dword ptr [ebp-4]
call @ILT+65(Base::Base) (00401046)
我们看到程序输出窗口输出:
那么我们可以分析:当调用子类的构造函数时,父类的构造函数也会被调用,我们在上一节也讲解了,如果我们没有在类中定义构造函数,编译器会自动为我们编译一个无参构造函数。