面向对象程序设计——类和对象

类和对象

类的构成

语法:

class 类名
  {
  private :
      私有成员函数或数据成员
  public :
      公有成员函数或数据成员
  protected : 
      被保护成员函数或数据成员
  }//注意这里的分号;
    //类成员函数的定义可以直接置于类定义中的花括号内。此时,成员函数将被缺省为按内联方式处理

类定义常见的两种形式:

类定义形式1:类界面和类实现分开。类实现在类外描述

类外的成员函数定义,我们一般使用如下格式:
返回值 类名::函数名(参数列表)
{
    函数体
}
举例:
    class CPerson
{
    private:
        char name[20];
        int age;
        char sex;
    public:
        void setname(char *nameval);   //set
        void setage(int ageval);
        int getage();     //get
}
void CPerson::setname(char *nameval);
{
	strcpy(name, nameval);     
}
int  CPerson::getage()   //只有通过方法才能得到私有成员的值
{
	return age;
}
char CPerson ::getsex()
{
	return  sex
}

类定义形式2:类界面和类实现全在类定义中完成

举例:
    class CPerson
{
    private:
        char name[20];
        int age;
        char sex;
    public:
        void setname(char* nameval)//设置属性,在定义类时定义函数体
        {
            strcpy(name, nameval);
        }}

​ 两者的区别:当类界面与类实现均在类定义中完成时,其成

员函数将默认为内联函数。

一般:当成员函数比较简单时应采用形式2;当成员函数较大时应采用形式1。

对象的概念和定义

概念:数据类型为类的变量称为类实例、类对象或对象实例。类对象和一般变量一样,在定义时分配内存。为每个类对象分配的内存中存放着每个类对象的数据成员的值。

类对象定义格式: 类名 类对象名列表

例:CPerson p1,p2;

访问

类成员的访问权限

公有成员:public成员,一般是成员函数,用于定义类的外部接口,在程序中的任何部分都可以访问。

私有成员:private成员,一般是数据成员,只能被类自身的成员函数访问。类成员在默认情况下是私有的

保护成员:protected成员,可以被该类的成员函数、以及该类的直接或间接子类的成员函数所访问

对象成员访问格式

类外只能访问公有数据成员,可通过公有函数接口访问私有数据成员。

对象名

  1. 对象名.数据成员名 p1.name
  2. 对象名.成员函数名(参数表) p1.setname(name); p1.getname();

对象指针

  1. 对象指针名->数据成员名 p->name;

  2. 对象指针名->成员函数名(参数表) p->setname(name); p->getname();

  3. *(对象指针名).数据成员名 (*p).name;

  4. *(对象指针名).成员函数名(参数表) (*p).setname(name); (*p).getname();

例:
    CPerson p1, p2;
	CPerson* p = &p2;          //同结构体访问成员
    p1.set("wangwu", 19, 'm');
    p->setname("limin");
    p->setage(18);
    p->setsex('F');
p1
    name:char* =wangwu
    age :int   =19
    sex :char  =M
p1
    name:char* =limin
    age :int   =18
    sex :char  =F    

类内访问成员的方法

  1. 访问自己的成员一个类的成员函数可以访问该类所以成员。

    格式:成员名。

  2. 访问其它类对象成员。只能访问公有成员

    格式:对象名.成员名 或 对象指针->成员名

  3. 访问全局变量或函数

    格式: ::变量名或函数名

this指针

定义:this指针指向用来调用成员函数的对象。 this本质就是指向本对象的一个指针。
this指向调用对象。

要引用整个对象,可以用*this

构造函数

定义

该类对象被创建的时候,编译系统对象分配内存空间,并自动调用该构造函数,由构造函数完成成员的初始化工作,故:构造函数的作用:初始化对象的数据成员

分类

●  无参构造函数

●  缺省构造函数

●  带默认值的构造函数

●  有参(无默认值)的构造函数

●  复制构造函数(拷贝构造函数)

		●	一种特殊的构造函数,当对象之间复制时会自动调用拷贝构造函数

		●   若类中没有显示定义拷贝构造函数,则系统会自动生成默认拷贝构造函数
		
!!!!注意:如果自己显示定义了一个构造函数,则不会再调用系统的构造函数,!!!!即默认的构造函数失效!!!!!
!!!!需要再自定义无参构造函数!!!!!
	#include <iostream>
	using namespace std;
	
	class Student
	{
        
	private:
		double c_x;
		double c_y;
	};
	
	public:
		// 无参构造函数
		// 如果创建一个类你没有写任何构造函数,则系统自动生成默认的构造函数,函数为空,什么都不干
		// !!!!注意:如果自己显示定义了一个构造函数,则不会再调用系统的构造函数,!!!!即默认的构造函数失效!!!!!
		// !!!!需要再自定义无参构造函数!!!!!
		Student()
		{
			c_x = 0;
			c_y = 0;
		}     
         //缺省构造函数
		Student()=default;

		// 一般构造函数
		Student(double x, double y):c_x(x), c_y(y){}   //列表初始化,仅构造函数可这样写,只有可在函数内用“=”的赋值才可以
		// 一般构造函数可以有多个,创建对象时根据传入的参数不同调用不同的构造函数
	
		Student(const Student& c)
		{
			// 复制对象c中的数据成员
			c_x = c.c_x;
			c_y = c.c_y;
		}
		double get_x()
		{
			return c_x;
		}
	
		double get_y()
		{
			return c_y;
		}

	int main()
	{
		// 调用无参构造函数,c1 = 0,c2 = 0
		Student c1, c2;
		// 调用一般构造函数,调用显示定义构造函数
		Student c3(1.0, 2.0);
		Student c5(c2);
		Student c4 = c2;    // 调用浅拷贝函数,参数为c2
		cout<<"c1 = "<<"("<<c1.get_x()<<", "<<c1.get_y()<<")"<<endl
			<<"c2 = "<<"("<<c2.get_x()<<", "<<c2.get_y()<<")"<<endl
			<<"c4 = "<<"("<<c4.get_x()<<", "<<c4.get_y()<<")"<<endl
			<<"c5 = "<<"("<<c5.get_x()<<", "<<c5.get_y()<<")"<<endl;
		return 0;
	}
	c1 = (1, 2)
	c2 = (0, 0)
	c4 = (0, 0)
	c5 = (0, 0)
	请按任意键继续. . .

析造函数

定义:

简单的说就是对象的初始化和销毁,初始化对应析造函数:在创建一个对象最开始要做的事情:如清0,赋初值,分配堆栈等等。而析造函数则是:在销毁对象时:如释放内存,销毁数据等等操作

如下面的例子:进入func()函数;创建了p1,于是打印了“初始化ok”。 执行完了后退出fuc()函数,于是打印了“退出”。这就是对象p1的创建于销毁。而在main中只只创建了p0,于是只打印了"初始化ok"

#include "iostream"
using namespace std;
 
class Person //类
{
public:
	Person() // 构造函数
	{
		cout << "初始化OK" << endl;
	};
 
	~Person() // 析构函数--- ~类名(){   }
	{
		cout << "退出" << endl;
	};
	
};
 
void func()
{
	Person p1;
}
int main()
{
	func();
	Person po;
	system("pause");
	return  0;
}

在这里插入图片描述

构造函数与析构函数执行关系

一般情况下,调用析构函数的次序正好与调用构造函数的次序相反: 最先被调用的构造函数,其对应的(同一对象中的)析构函数最后被调用,而最后被调用的构造函数,其对应的析构函数最先被调用。

在这里插入图片描述

拷贝构造函数

● 拷贝构造函数是一种特殊的构造函数,具有单个形参,该形参(常用const修饰)是对该类型的引用。当定义一个新对
象并用同一类型的对象都它进行初始化时,将显示使用拷贝构造函数,当该类型的对象传递给函数返回该类型的对象时,
将隐调用拷贝构造函数
当类中有一个数据成员是指针时,或者有成员表示在构造函数中分配的其他资源,必须显示定义拷贝构造函数
● 构造函数的使用情况
一个对象以值传递的方式传入函数体
一个对象以值传递的方式从函数体返回
一个对象需要通过另一个对象进行初始化

#include <iostream>
	using namespace std;
	
	class Test
	{
	private:
		int t_a;
	};
	
	public:
		// 构造函数
		Test(int a):t_a(a){
			cout<<"creat: "<<t_a<<endl;
		}
		// 拷贝构造函数
		Test(const Test& T)
		{
			t_a = T.t_a;
			cout<<"copy"<<endl;
		}
		// 析构函数
		~Test()
		{
			cout<<"delete: "<<t_a<<endl;
		}
		// 显示函数
		void show()
		{
			cout<<t_a<<endl; 
		}
	// 全局函数,传入的是对象
	void fun(Test C)
	{
		cout<<"test"<<endl;
	}
	int main()
	{
		Test t(1);
		// 函数中传入对象
		fun(t);
		return 0;
	}
	creat: 1
	copy
	test
	delete: 1
	delete: 1
	请按任意键继续. . .

浅拷贝与深拷贝

浅拷贝
所谓浅拷贝,指的是在对象复制时,只对对象中的数据成员进行简单的赋值,默认拷贝构造函数执行的也是浅拷
贝。也就是增加了一个指针,指向原来已经存在的内存。 正常情况下,“浅拷贝”已经能很好的工作,但是一旦对
象存在动态成员(或指针变量成员),浅拷贝就会出问题。让我们考虑下面一段代码:

#include <iostream>
#include <assert.h>    
using namespace std;
class Test
	{
        private:
		int x;
		int y;
		int* p;
	public:
		Test(){
			p = new int(10);
		}
		~Test(){
			assert(p != NULL);     // assert()作用是如果他的条件返回错误,则终止程序执行 
			delete p;
		}
	};
	int main()
	{
		Test t1;
		Test t2(t1);    // 调用默认拷贝构造函数
		return 0;
}

上述程序崩溃。在使用t1复制t2时,进行的是浅拷贝,只是将成员的值进行赋值。此时,t1.p = t2.p, 即两个指针指向了堆里的同一个空间。这样,析构函数会被调用两次,这就是错误出现的原因。此问题的解决方法是“深拷贝”。

深拷贝
深拷贝就是对于对象中的动态成员,并不只是简单的赋值,而是重新分配空间,即资源重新分配。上述代码处理如下:

#include <iostream>
#include <assert.h>    
using namespace std;
class Test
{
    private:
		int x;
		int y;
		int* p;
	public:
		Test(){
			x = 0;
			y = 0;
			p = new int(10);
		}
		Test(const Test& t)
		{
			x = t.x;
			y = t.y;
			p = new int(10);//注意
			*p = *(t.p);/注意
		}
		~Test(){
			assert(p != NULL);     // assert()作用是如果他的条件返回错误,则终止程序执行 
			delete p;
		}
		int get_x(){return x;}
		int get_y(){return y;}
};
int main()
{
		Test t1;
		Test t2(t1);    // 调用默认拷贝构造函数
		cout<<"("<<t1.get_x()<<", "<<t1.get_y()<<")"<<endl
			<<"("<<t2.get_x()<<", "<<t2.get_y()<<")"<<endl;
		return 0;
}
(0, 0)
(0, 0)
请按任意键继续. . .

此时t1与t2的p各自指向一段内存空间,但他们指向的内容相同,这就是“深拷贝”。

复合类

复合类也称为组合类,是指将一个类的对象作为另一个类的成员变量。

例如:CPerson有注册日期、出生日期等属性。
      注册日期、出生日期:年月日

      时钟有时针、分针、秒针组成。
      时针、分针、秒针:循环计数器

#include<iostream>
using namespace std;
class Date{
	private:
		int y,m,d;
	public:
		Date();
		Date(int y,int m,int d);
		void setdate(int y,int m,int d);
};
Date::Date(int y1,int m1,int d1):y(y1),m(m1),d(d1){}
void Date::setdate(int y1,int m1,int d1){
	y=y1,m=m1,d=d1;
}
class CPerson{
	private:
		 Date birth;
	public:
		CPerson();
		CPerson(int y,int m,int d);
};
CPerson::CPerson(int y,int m,int d){
	birth.setdate(y,m,d);
}
//或者用构造函数:
//CPerson::CPerson(int y,int m,int d):birth(y,m,d) {}

对象数组

对象数组的定义:
所谓对象数组,指每一个数组元素都是对象的数组,即若一个类有若干个对象,我们把这一系列的对象用一个数组来存放。对象数组的元素是对象,不仅具有数据成员,而且还有函数成员。

​ 定义一个一维数组的格式如下:
​ 类名 数组名[下标表达式]

​ 与基本数据类型的数组一样,在使用对象数组时也只能访问单个数组元素,其一般形式为:
数组名[下标].成员名

在建立数组时,同样要调用构造函数。有几个数组元素就要调用几次构造函数

静态数组

一个参数:

#include<iostream>
using namespace std;
class exam{
    private:
		int x;
	public:
		exam(int n){  //只有1个参数的构造函数 
			x=n;
		}
		int get_x(){
			return x;
		}
};
int main(){
	exam ob1[4]={11,22,33,44};  //用只有1个参数的构造函数给对象数组赋值 
	for(int i=0;i<4;i++)
		cout<<ob1[i].get_x()<<" ";
	return 0;
}

多个参数:

#include<iostream>
#include<cmath>
using namespace std;
class Complex{
	public:
		Complex(double r=0.0,double i=0.0):real(r),imag(i){
		}
		~Complex(){
			cout<<"Destructor called."<<endl;
		}
		double abscomplex(){
			double t;
			t=real*real+imag*imag;
			return sqrt(t);
		}
	private:
		double real;
		double imag;
};
int main(){
	Complex com[3]={  //定义对象数组
		Complex(1.1,2.2),  //调用构造函数,为第1个对象数组元素提供实参1.1和2.2 
		Complex(3.3,4.4),  //调用构造函数,为第2个对象数组元素提供实参3.3和4.4 
		Complex(5.5,6.6)  //调用构造函数,为第3个对象数组元素提供实参5.5和6.6	
	};
	cout<<"复数1的绝对值是:"<<com[0].abscomplex()<<endl;
	cout<<"复数1的绝对值是:"<<com[1].abscomplex()<<endl;
	cout<<"复数1的绝对值是:"<<com[2].abscomplex()<<endl;
 	return 0;
}

动态对象数组

​ 例:

第一种

int n;
cin>>n;
Point*pp=new Point[n];
	for(int i=0;i<n;i++){
		(pp+i)->setXY();//或pp[i].setXY();
	}

第二种

cpoint *p = (cpoint *)operator new[](3 * sizeof(cpoint));
for (int i = 0; i < 3; i++)
{
      cin >> x >> y;
      new(&p[i]) cpoint(x,y);    // 有参构造函数构造
}
for(int i=0; i<3; i++)
      p[i].~cpoint();      //析构对象
operator delete(p);	 //释放空间
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值