目录
1.面向过程与面向对象的初步认识
2.类的引入
3.类的定义
4.类的访问限定符及封装
5.类的作用域
6.类的实例化
7.类的对象的大小的计算
8.类成员函数的this指针
面向对象和面向过程是两种编程思想。面向过程是一种以事件为中心的编程思想,编程的时候把解决问题的步骤分析出来,然后用函数把这些步骤实现,在一步一步的具体步骤中再按顺序调用函数。
而面向对象是一种以“对象”为中心的编程思想,把要解决的问题分解成各个对象,建立对象的目的不是为了完成一个步骤,而是为了描叙某个对象在整个解决问题的步骤中的属性和行为。
面向过程和面向对象各有优缺点。面向过程强调代码的短小精悍,善于结合数据结构来开发高效率的程序。但需要深入思考,耗费精力,代码重用性低,扩展能力差,后期维护难度比较大。而面向对象程序设计中代码间的相关性低(低耦合特性),使得代码很容易被复用和扩展,同时也说明了面向过程的代码重用性低、扩展能力差。但开销大,当要修改对象内部时,对象的属性不允许外部直接存取,所以要增加许多没有其他意义、只负责读或写的行为。这会为编程工作增加负担,增加运行开销,并且使程序显得臃肿。
typedef int datatype;
//利用结构体定义栈,内部可定义成员函数
struct Stack
{
//初始化
void stinit(int _capacity)
{
arr = (datatype*)malloc(sizeof(datatype) * _capacity);
if (arr == nullptr)
{
return;
}
size= 0;
capacity = _capacity;
}
//入栈
void stackpush(const datatype& _data)
{
arr[size] = _data;
size++;
}
void stackpop()
{
size--;
}
datatype gettop()
{
return arr[size-1];
}
void destroyst()
{
if (arr!= nullptr)
{
free(arr);
arr = nullptr;
size = capacity = 0;
}
}
int* arr;
int size;
int capacity;
};
int main()
{
struct Stack ST;
ST.stinit(5);
ST.stackpush(1);
ST.stackpush(2);
ST.stackpush(3);
ST.stackpush(4);
cout << ST.gettop() << endl;
ST.stackpop();
cout << ST.gettop() << endl;
return 0;
}
如上我们定义了成员函数和成员变量,可以值间接调用成员函数。在这里struct 已经作为一个类在用了。
在上述我们知道了struct 也可作为类,那么类的定义是什么?什么样的结构叫做类?
其实类是一种用户自定义的数据类型,它描述了一组有相同特性(属性)和相同行为(方法)的一组对象的集合。一个类的定义通常包含两部分的内容,一是该类的属性,另一部分是它所拥有的方法。这样的结构类型就可叫做类。
而对于c++,除了常用的结构体是类,c++也提供了一个关键字class专门用来定义类。这也是大多数人选择的方法。
class className
{
// 类体:由成员函数和成员变量组成
}; // 一定要注意后面的分号
同样是类,为什么在选择类的定义是,人们一般不用struct,而取用clss,这里就要说到一点他们的访问限定符的区别。
在C++中,可以使用以下访问修饰符在进行声明时指定类型或成员的可访问性:public、protected、private。
.public: 可以被该类中的函数、子类的函数、其友元函数访问,也可以由该类的对象访问。
.protected: 可以被该类中的函数、子类的函数、以及其友元函数访问,但不能被该类的对象访问。
.private: 只能由该类中的函数、其友元函数访问,不能被任何其他访问,该类的对象也不能访问 。
而对于我们刚才说到的struct,它的默认访问限制是public,外部都是可以访问的。而对于class,它的默认访问限制是private,一般除了类中的是无法访问的。而对于真正实现大项目时,为了防止相互可能存在访问的情况下,private对于程序员来说更好。这就是为什么大多数人选择class,其次在继承和模板参数列表位置,struct和class也有区别。
注意:访问限定符只在编译时有用,当数据映射到内存后,没有任何访问限定符上的区别
封装:
首先,面向对象的三大特性分别是:封装、继承、多态。
在类和对象阶段,主要是研究类的封装特性,那什么是封装呢:
封装是指一种将抽象性函数接口的实现细节部分包装、隐藏起来的方法。封装可以被认为是一个保护屏障,防止该类的代码和数据被外部类定义的代码随机访问。要访问该类的代码和数据,必须通过严格的接口控制。封装最主要的功能在于我们能修改自己的实现代码,而不用修改那些调用我们代码的程序片段。适当的封装可以让程式码更容易理解与维护,也加强了程式码的安全性。
在C++语言中实现封装,可以通过类将数据以及操作数据的方法进行有机结合,通过访问权限来
隐藏对象内部实现细节,控制哪些方法可以在类外部直接被使用。
对于类的内联函数,还是一样,函数不会进入符号表,直接展开。
对于类和一般定义的空间的内部的访问,他们一般都是有空间访问限制的,这就需要在访问时加上结构::成员,像这种结构就跟之前学的namespace的定义一样,在实现struct class这种结构时,其实也定义了空间。是不被外部访问的,需要借助域访问作用符。
定义的类就是一种类型,我们可以通过这种类实例化多个对象。
class Person
{
public:
//方法一
//方法二
//方法三
private:
//他的属性
char* name;
int age;
int scoer;
int id;
};
int main()
{
Person p1;
Person p2;
Person p3;
}
对于一个类,它的变量只是声明,所有的成员函数不在类中存储且只是声明,它们会被提供一块公共的代码区域,一面每个对象都能访问且不同。
class A1 {
public:
void f1() {}
private:
int _a;
char c;
};
//只有成员函数
class A2
{
public:
void f2() {}
void f3() {}
};
// 类中什么都没有---空类
class A3
{};
int main()
{
cout << sizeof(A1) << endl;
cout << sizeof(A2) << endl;
cout << sizeof(A3) << endl;
return 0;
}
根据编译的结果可以看到成员函数实际上是不计入大小的,只记入成员变量。空类认为是1。
1.this指针的特性:类型:类类型*const,在成员函数中无法赋值,无法作为实体参数。
2.只能在成员函数中使用
实际上是通过传入对象的指针来确定对函数的调用,类中定义的仅仅是个声明,不可能定义多个对象都去访问类中的变量。
其次我们虽然知道是有个this指针,但不可以直接传个对象指针作为this指针,this指针可以在函数内部访问成员变量会其他成员函数,但不能实体化作为参数,他是隐形的。
注意事项:
上述三种给三种情况 ,A .编译错误 B.运行崩溃 C.正常运行
对于第一个,可以看到定义了一个对象指针 且为空,之后利用对象指针访问函数。首先我们会想到空指针访问需要解引用,但不会报编译错误,排除A,但实际上选C,正常运行:因为成员函数不存放在类中且也不存放在对象中,(存在对象中会是对象占良大量空间),成员函数实际是存在一块公共代码区域的,调用函数直接拿函数名去找这个函数的地址,因此并会对对象指针解引用,直接去访问函数。
第二个,与第一个原理类似,这里也不会报错,即使我们这样去写,但编译器不会解引用指针,也是直接那函数名去公共代码空间哪里找。
第三个,会运行崩溃,可以发现打印中会去访问对象的成员变量,空指针会去解引用,因此会运行崩溃。
其次在vs传递this指针时是通过寄存器来传递的。