文章目录
1、面向过程和面向对象
(1)面向过程编程(C语言)
分析出解决问题的步骤,再用函数将这些步骤一一实现,使用的时候依次调用,面向过程的核心是过程。
优点:性能比面向对象好,因为类调用时需要实例化,开销比较大,比较消耗资源。比如单片机、嵌入式开发、 Linux/Unix等一般采用面向过程开发,性能是最重要的因素。
缺点: 缺点:不易维护、不易复用、不易扩展。
(2)面向对象编程(Java、C++)
把构成问题的事物分解成一个个对象,建立对象不是为了实现某一个步骤,而是为了描述某个事物在解决问题中的行为,面向对象的核心是对象。
优点:易维护、易复用、易扩展,由于面向对象有封装、继承、多态性的特性,可以设计出低耦合的系统,使系统更加灵活、更加易于维护。
缺点:性能比面向过程低。
举例:人吃饭
面向过程:强调的是吃饭的这个过程,人只是一个参数。
面向对象:强调的是人,吃饭只一个其中一个的动作。
2、面向对象的四(三)大特征
(1)抽象
将具体的问题(对象)进行概括,抽出这类对象的共同特征(静态、动态),叫做抽象。举例:人类这个概念就是抽象出来的。动态:说话,吃饭,思考,静态:头,手,足等。
(2)封装
将抽象出来的共同特征(静态、动态)包装在一起成为一个整体,把数据(变量)和操作数据的方法(函数)封装在一起,对自己信任的对象提供操作,对不信任的对象隐藏。封装的实现:类class。
(2)继承
是指让某个类型的对象获取另一个类型的对象的属性的方法。
(3)多态
是指向不同的对象发送同一个消息,会产生不同的行为。
3、类
类是具有相同属性和行为的一组对象的集合,它为属于该类的对象提供统一的抽象描述,其内部包含了属性(静态特征)和行为(动态特征)。
类的声明:
class 类名(首字母一般是大写)
{
public: //公有成员,是对外部的接口
private: //私有成员
protected: //保护成员
}; //分号不要忘记
公有成员:用关键字public修饰,它是类与外部的接口,类外可以访问公有成员,必须通过对象。
私有成员:用关键字private修饰,只允许类中成员访问,而类外不能直接访问私有成员。
保护成员:用关键字protected修饰,特点跟私有成员一致,在继承时对派生类的影响不同。
注意:一个类中不是必须要包含三种成员
class A 注意与struct的区别
{
int a; //默认是私有成员
}
//钟表类
class Clock
{
public:
void set_time(); //成员函数
void get_time();
private:
int hour; //成员数据
int minute;
int second;
protected:
};
成员数据:跟普通变量没有区别,只是声明在类中
成员函数:在类中声明的函数,在哪个地方实现呢?
(1)在类中实现,自动形成内联函数
class Clock
{
public:
//在类中实现的函数,会自动形成内联函数
void set_time(int h=0,int m=0,int s=0) //成员函数
{
hour = h;
minute = m;
second = s;
}
void get_time();
private:
int hour; //成员数据
int minute;
int second;
protected:
private:
};
(2)在类外实现,实现时要用“类名::”指定是属于哪个类的函数
void Clock::get_time()
{
cout << hour << ":" << minute << ":" << second << endl;
}
4、对象
对象是一个类的示例,即类的变量
声明形式:
类名 对象名
如:
Clock myclock;
Clock *myclock = new Clock;
成员函数的访问方式:
类中成员互访:直接使用成员名
类外访问成员:
使用“对象名.成员名” 或 “对象名->成员名”
只能直接访问公有成员
Clock myclock1;
myclock1.set_time(12,30,00);
myclock1.get_time();
Clock *myclock2 = new Clock;
myclock2->set_time(12,30,00);
myclock2->get_time();
delete myclock2;
myclock2 = NULL;
5、构造函数
作用:在创建对象时,对成员数据进行初始化
声明形式:
类名(参数列表)
{
}
如:
Clock(int h=0,int m=0,int s=0) //构造函数
{
hour = h;
minute = m;
second = s;
}
特点:
(1)在创建对象时,由系统自动调用
(2)如果没有声明构造函数,那么系统会生成一个默认的构造函数(没有作用)
(3)构造函数可以声明为内联函数,重载函数,带默认参数的函数
(4)构造函数可以有参数,但是不能写返回类型
(5)构造函数不能为虚函数,可以类内声明,类外构造
//构造函数
Clock(int h=0,int m=0,int s=0)
{
cout << "构造函数\n";
hour = h;
minute = m;
second = s;
}
//Clock myclock1(); //不传参数时,加括号和不加括号是有区别
Clock myclock1;
myclock1.get_time();
Clock *myclock2 = new Clock(); //不传参数时,加括号和不加括号是没有区别
myclock2->get_time();
delete myclock2;
myclock2 = NULL;
6、析构函数
作用:在对象删除时,进行清理工作
声明形式:
~类名()
{
}
特点:
(1)在删除对象时,由系统自动调用
(2)如果没有声明析构函数,那么系统会生成一个默认的析构函数(没有作用)
(3)一个类中有且仅有一个析构函数
(4)析构函数不写参数和返回类型
(5)析构函数可以为虚函数,且某些情况下,必须为虚函数
7、拷贝构造函数
作用:
拷贝构造函数是一种特殊的构造函数,其形参为类对象的引用,如果没有声明拷贝构造函数,那么系统会生成一个默认的拷贝构造函数(有作用)
声明:
类名(const 类名 &对象名);
如:
Clock(const Clock &myclock):t(t)//若t被const修饰则使用:()初始化参数
{
}
何时会调用拷贝构造函数?
①用一个对象去初始化另一个对象时,会自动调用拷贝构造函数
Clock myclock2(myclock1); //拷贝构造函数,复制
myclock2.get_time();
Clock myclock3 = myclock1; //拷贝构造函数,复制
myclock3.get_time();
Clock myclock4;
myclock4 = myclock1; //赋值函数,赋值
myclock4.get_time();
②如果函数的参数是类对象时,调用该函数,会自动调用拷贝构造函数
③如果函数的返回值是类对象时,调用该函数,会自动调用拷贝构造函数
void fun1(Clock myclock) //参数是类对象
{
}
调用:fun1(myclock1); //调用拷贝构造函数
void fun11(Clock &myclock) //参数是类对象的引用
{
}
调用:fun11(myclock1); //不会调用拷贝构造函数
④如果函数的参数是类对象的引用时,调用该函数,会自动调用拷贝构造函数吗?不会
⑤如果函数的返回值是类对象的引用时,调用该函数,会自动调用拷贝构造函数吗?不会
Clock fun2(void) //返回值是类对象
{
Clock myclock1;
return myclock1;
}
myclock4 = fun2(); //调用拷贝构造函数
Clock & fun22(void) //返回值是类对象的引用
{
static Clock myclock1;
return myclock1;
}
myclock4 = fun22(); //不会调用拷贝构造函数
深拷贝和浅拷贝:
浅拷贝:默认的拷贝构造函数都是浅拷贝,只是对值进行了拷贝
深拷贝:如果在类中,在堆上申请了空间,那么必须重写拷贝构造函数,实现空间的拷贝。
注意:
一般情况下,浅拷贝不需要重写拷贝构造函数,使用默认的拷贝构造函数即可,但是在堆上申请了空间,就必须进行深拷贝,除了值的拷贝,要重新申请空间。
class Clock
{
public:
//构造函数
Clock(int h=0,int m=0,int s=0)
{
cout << "构造函数\n";
hour = h;
minute = m;
second = s;
p = new int(5);
}
//析构函数
~Clock()
{
cout << "析构函数\n";
delete p;
p = NULL;
}
//拷贝构造函数(深拷贝)---------------------------------------------------------
Clock(const Clock &myclock)
{
cout << "拷贝构造函数\n";
hour = myclock.hour;
minute = myclock.minute;
second = myclock.second;
p = new int;
*p = *myclock.p;
}
void get_time();
private:
int hour; //成员数据
int minute;
int second;
protected:
int *p;
};
8、赋值函数
声明形式:
类名 & operator=(const 类名 &对象名);
如:
Clock & operator=(const Clock &myclock)
如果没有声明赋值函数,那么系统会生成一个默认的赋值函数(有作用)
9、this指针
作用:用于指向本类对象,只有非静态的成员函数才有this指针,这个指针是成员函数中隐藏的参数
如:
Clock(this,int h,int m,int s) //this是隐藏的,不需要写出来
{
}
this的使用情况:
(1)函数的参数和成员数据同名时
Clock(int hour=0,int minute=0,int second=0) //成员函数
{
this->hour = hour;
this->minute = minute;
this->second = second;
}
(2)函数的返回值为类对象时,可以使用this指针
Clock fun()
{
Clock myclock;
return *this;
}
10、静态数据static
静态成员数据:
在成员数据前加上static关键字
特点:
(1)静态成员数据必须在类外初始化,通过“类名::”限定属于哪个类
(2)该类的所有对象维护该成员的同一个拷贝,类似于C中static修饰局部变量,只会被初始化一次。
静态成员函数:
在成员函数前加上static关键字
特点:
(1)在类外,普通成员函数只能通过“对象名+函数名”进行访问;而静态成员函数既可以通过“对象名+函数名”进行访问,又可以通过“类名::函数名”进行访问。
(2)静态成员函数只能访问静态成员
(3)静态成员函数没有this指针
11、const数据
常量:const int a;
常指针:const int *p;
常数组:const int arr[5];
常引用:const int &b = a;
常对象:const Clock myclock(10,10,10) ; // 常对象必须初始化
常成员数据:在成员数据前用const修饰,如:const int a;
常成员函数:在成员函数后用const修饰,如:void fun() const;
特点:
(1)常成员数据在构造函数的参数列表初始化,普通数据数据也可以
如:Clock(int h=0,int m=0,int s=0):a(10),b(20)
(2)常成员函数不能修改任何变量的值,既可以访问普通数据,也可以访问const数据
(3)常对象必须初始化
(4)常对象只能调用常成员
12、友元函数
声明方式:在函数前加上“friend”关键字修饰,如:friend void fun();
注意:在类中声明,但不是成员函数
作用:友元函数可以访问类的私有成员和保护成员,但必须通过类对象,破坏了封装性,但增强了灵活性,让程序员在封装性和快速性方面进行合理的选择。
class A
{
public:
friend void display(A a); //声明为A的友元函数
A(int a = 0)
{
this->a = a;
}
~A()
{
}
private:
int a;
};
void display(A a)
{
cout << a.a << endl;
}
eg:计算两个点之间的距离
Point p1(1,2),p2(2,4);
Distance();
class Point
{
public:
//friend void display(A a); //声明为A的友元函数
Point(int x = 0,int y = 0)
{
this->x = x;
this->y = y;
}
~Point()
{
}
friend float Distance(Point &p1,Point &p2);
private:
int x,y;
};
float Distance(Point &p1,Point &p2)
{
return sqrt(pow((p1.x-p2.x),2)+pow((p1.y-p2.y),2));
}
int main(void)
{
Point p1(1,2),p2(2,4);
cout << "距离为:" << Distance(p1,p2) << endl;
}
13、友元类
若一个类是另一个类的友元类,那么该类的所有成员函数都是另一个类的友元函数。
如:A是B的友元类,A的所有成员函数都是B的友元函数
声明:
class A
{
friend class B; //声明B是A的友元类
};
设置为互为友元类:
class B; //前向引用声明
class A
{
friend class B; //声明B是A的友元类
void fun(B b); //要访问B的私有和保护成员,就必须传B的对象
};
class B
{
friend class A; //声明A是B的友元类
void fun(A a); //要访问A的私有和保护成员,就必须传A的对象
};
说明:
(1)友元类是单向的
(2)友元类不具有传递性,如A是B的友元类,B是C的友元类,A不一定是C的友元类
(3)友元类不能继承,如B是A的儿子,C是B的友元类,C不一定A的友元类
14、智能指针
背景:如果使用普通指针,当用new在堆上申请空间之后,就必须调用delete去释放空间,否则会造成内存泄漏(一个资深的程序员是不会犯这种低级错误的),麻烦的是不知道在哪里去释放空间,因为这片内存可能会在多处被使用(多线程)。
如果使用智能指针,那么就不用调用delete,空间使用完毕之后,会自动释放空间。
C++中智能指针:auto_ptr(被弃用),unique_ptr,shared_ptr,weak_ptr
#include <iostream>
#include <cstring>
#include <memory> //智能指针
using namespace std;
class A
{
public:
friend void display(A a); //声明为A的友元函数
A(int a = 0)
{
this->a = a;
}
~A()
{
cout << "析构函数\n";
}
void show()
{
cout << "a = " << a << endl;
}
private:
int a;
};
int main(void)
{
A *a1 = new A(10); //普通指针,必须调用delete释放空间
a1->show();
shared_ptr<A> a2(new A(20)); //智能指针,不需要调用delete释放空间
a2->show();
delete a1;
a1 = NULL;
}
参考: