C++学习类与对象

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;
}


参考:

智能指针

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Yengi

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值