我们学过C语言,其就是一种面向过程的编程流程,以事件为中心,解决事件时分析出流程,然后用函数实现一系列流程,然后按照流程一步一步按顺序调用函数。
优点:流程化使得编程任务明确,在开发之前基本考虑了实现方式和最终结果,具体步骤清楚,便于节点分析。
效率高,面向过程强调代码的短小精悍,善于结合数据结构来开发高效率的程序。
缺点:需要深入的思考,耗费精力,代码重用性低,扩展能力差,后期维护难度比较大。
在编程时,一些简单问题可以通过面向过程来解决,但是当问题规模变大的时候就有困难了。编程本质来源于现实生活,我们的生活中可以看成一个一个对象,每个对象拥有一定的属性,这就引入了面向对象编程的概念,面向对象是一种以“对象”为中心的编程思想,把要解决的问题分解成各个对象,建立对象的目的不是为了完成一个步骤,而是为了描叙某个对象在整个解决问题的步骤中的属性和行为。
优点:
结构清晰,程序是模块化和结构化,更加符合人类的思维方式;
缺点:
开销大,当要修改对象内部时,对象的属性不允许外部直接存取,所以要增加许多没有其他意义、只负责读或写的行为。这会为编程工作增加负担,增加运行开销。
封装是C++中最基本的一个属性,但是封装从何而来呢?C++将对象的属性和操作都放入一个类里,这就是其面向对象的本质。
模板:
class 类型名称 {
public:成员列表;
private:成员列表;
protected:成员列表;
};
以上便是类的模板,模板中给了三个不同的数据类型,public是公有成员,可以从外部访问(例:此处的成员可以直接在主函数中调用),而private(私有的),protected(保护的)中的成员不能直接调用,只能通过公有函数进行访问改变其属性,这就体现了C++的封装特性。
类是一种数据类型,定义时系统不为其分配存储空间,所以不能对类的数据从成员进行初始化。也不能使用extern,auto,register关键字限定其存储类型。
成员函数可以直接使用类定义中的任意成员,可以处理数据成员,也可以调用函数成员,成员函数的定义:
类中一般只对成员函数进行声明,函数定义通常在类的说明之后进行,格式:
返回值类型 类名::函数名(参数表) {}
//我们可以举例如下:
class mybook {
public:
void my_printf(char* name,int priace,int math,int nums);
private:
char name[10];//书名
int priace;//单价
int math;//数量
int nums;//总价
};
void mybook::my_printf(char* name,int priace,int math,int nums) {
cout<<"name"<<name<<"price"<<price<<"math"<<math<<"nuns"<<nums<<endl;
}
类只是对象的模板,就像盖房子图纸,有了类不一定存在对象,只是告诉编译器可以通过类可以定义对象(盖房子),为在内存中开辟出该对象的实例,
定义对象时我们可以直接通过类名来定义一个对象。
int a;
mybook java;
我们通过mybook类型名创建了一个对象-》java,同时为他分配了一定的存储空间来存放其数据和操作函数(代码)。
对象的使用很简单,就是在对象名后加上’.',紧接着加上成员函数名,和调用函数一样加上实参即可,但是此处必须是公有成员函数,私有和受保护的只能在成员函数中被调用,不能在外部直接调用。
当定义对象数量为4时,我们的对象属性很显然会在数据区申请到4份存放数据(对象属性)的区域,而不同之处在于其操作属性的行为,也就是函数,成员属性我们能理解,每个对象是独立的,成员属性也应该是独立的,但是成员函数是操作对象属性的行为,然后对象属性相同,因此如果在代码区存放4份成员函数就会显得浪费内存,所以此处有两种形式,一是对象拥有独立的代码区存放成员函数,二是对象公用一份成员函数的代码,放在公共代码区。
class Int
{
int value;
public:
Int(int x = 0) :value(x) { cout << "Create Int Object " << this << endl; }
Int(const Int& it) :value(it.value) { cout << "Copy Create Int Object" << this << endl; }
Int& operator=(const Int& it)
{
if (this != &it)
{
value = it.value;
}
return *this;
}
~Int() { cout << "Destroy Int " << this << endl; }
void PintInt() const { cout << value << endl; }
};
int main()
{
Int a(10), b(20), c;
Int d(b);
c=a;
c.PintInt();
return 0;
}
编译器对程序员自己设计的类型分三次编译:
第一:识别记录类体种的属性名称,类型和访问权限,与属性在类体中的位置无关。
第二:识别和记录类体中函数原型(返回类型+函数名+参数表),形参的默认值,访问限定,不识别函数体。
第三:改写在类中定义函数的参数列表,改写对象调用成员函数的形式。
在成员函数中一般都会存在this指针,例如构造函数,拷贝构造函数等
如上代码:我们以运算符重载函数为例,c=a <=>c.operator=(a)<=>operator(&c,a);我们可以看出c对象在调用运算符=重载函数时,此处this指针指向对象c,一般函数会省略掉this指针,我们可以还原函数编译阶段的参数,
Int& operator=(const Int& it)
//Int& operator(Int *const this,cosnt Int & it );而this指针便指向我们调用函数的对象
this指针特点:
类的数据成员多为私有的,不能从外部进行操作,因此需要通过公有的成员函数进行操作赋值,而构造函数便是再创建对象的同时进行初始化的函数,注意只能初始化一次。
作用:
构造函数的特点:
class Int {
private: int value;
public:
Int(int c=0) {
this->value=c;
}
};
当创建对象时,系统会自动调用构造函数从而进行初始化,当然当对象生命周期结束的时候页会自动调用析构函数,对对象进行注销并且做一系列善后工作,这就是析构函数。
class mystring {
private: char* ptr;
public:
mystring(const char* s=nullptr) {
if(this!=nullptr) {
int len=strlen(s)+1;
ptr=new char[len];
strcpy_s(ptr,len,s);
}else {
ptr=new char('\0');
}
}
~mystring() {
delete[]ptr;
ptr=nullptr;
}
};
构造函数和析构函数的区别是:1、构造函数可以接收参数,能够在创建对象时赋值给对象属性,析构函数不能带参数;2、创建对象时调用构造函数,析构函数是在销毁对象时自动调用的。
class mystring {
private: char* ptr;
public:
mystring(const char* s=nullptr) {
if(this!=nullptr) {
int len=strlen(s)+1;
ptr=new char[len];
strcpy_s(ptr,len,s);
}else {
ptr=new char('\0');
}
}
~mystring() {
delete[]ptr;
ptr=nullptr;
}
};
void fun() {
mystring d("ace");//局部对象
static mystring e("afa");//静态局部对象
}
mystring a;//全局对象
int main() {
mystring b("hello");
mystring *c=new mystring{"xxin"};//动态创建对象
delete c;
mystring q;
new(q) mystring("xxin");//定位new
fun();
}
拷贝构造就是用一个对象的数据拷贝一份初始化另一个对象,这个过程需要拷贝数据成员。在建立对象的时候用一个已存在的对象来初始化该对象的内存空间,这就是拷贝构造函数。
mystring(const mystring& it) {
int len=strlen(it.ptr);
ptr=new char[len];
strcpy_s(ptr,len,it.ptr);
}
我们知道在创建字符串这个类的时候,字符串存在于堆中,而对象中只存在一个指向堆区字符串的指针,正所谓浅拷贝就是在创建这个对象时,仅仅将这个对象的指针指向待拷贝对象的堆区字符串,两者公用一块堆区空间,记住此时只能释放一次,否则程序会崩掉。而深拷贝就是将堆区资源也进行了一份拷贝,在堆区重新申请了一块空间,待拷贝字符串复制一份存放在这块空间中,对象的指针指向这块新的堆区空间。大部分拷贝都是深拷贝,大家也可以用浅拷贝仿写一下mystring类并实现代码。
运算符重载我觉得本质上是一种特殊的函数重载,一般为类的成员函数,