类和对象基本知识及对象的析构和构造

基本概念

1)类、对象、成员变量、成员函数

2)面向对象三大概念

封装、继承、多态


类的封装

1)封装(Encapsulation)

A)封装,是面向对象程序设计最基本的特性。把数据(属性)和函数(操作)合成一个整体,这在计算机世界中是用类与对象实现的。

B)封装,把客观事物封装成抽象的类,并且类可以把自己的数据和方法只让可信的类或者对象操作,对不可信的进行信息隐藏。

         备注:有2层含义(把属性和方法进行封装对属性和方法进行访问控制)

         C++中类的封装

成员变量,C++中用于表示类属性的变量

成员函数,C++中用于表示类行为的函数


2)类成员的访问控制

在C++中可以给成员变量和成员函数定义访问级别

Public修饰成员变量和成员函数可以在类的内部和类的外部被访问

Private修饰成员变量和成员函数只能在类的内部被访问


3)struct和class关键字区别

在用struct定义类时,所有成员的默认属性为public

在用class定义类时,所有成员的默认属性为private







对象的构造和析构

前言

创建一个对象时,常常需要作某些初始化的工作,例如对数据成员赋初值。注意,类的数据成员是不能在声明类时初始化的。

为了解决这个问题,C++编译器提供了构造函数(constructor)来处理对象的初始化。构造函数是一种特殊的成员函数,与其他成员函数不同,不需要用户来调用它,而是在建立对象时自动执行。


造函数和析构函数的概念

1.有关构造函数

1构造函数定义及调用

1)C++中的类可以定义与类名相同的特殊成员函数,这种与类名相同的成员函数叫做构造函数;

2)构造函数在定义时可以有参数;

3)没有任何返回类型的声明。

2构造函数的调用

自动调用:一般情况下C++编译器会自动调用构造函数

手动调用:在一些情况下则需要手工调用构造函数

 

有关析构函数

3)析构函数定义及调用

         1)C++中的类可以定义一个特殊的成员函数清理对象,这个特殊的成员函数叫做析构函数

语法:~ClassName()

2)析构函数没有参数也没有任何返回类型的声明

3)析构函数在对象销毁时自动被调用

4)析构函数调用机制

         C++编译器自动调用


#define  _CRT_SECURE_NO_WARNINGS 
#include <iostream>
using namespace std;

class Test
{
public:
	Test()  //无参数 构造函数
	{
		a = 10;  //作用完成对属性的初始化工作
		p = (char *)malloc(100);
		strcpy(p, "aaaaffff");
		cout<<"我是构造函数 被执行了"<<endl;
	}
	void print()
	{
		cout<<p<<endl;
		cout<<a<<endl;
	}
	~Test() //析构函数
	{
		if (p != NULL)
		{
			free(p);
		}
		cout<<"我是析构函数,被调用了" <<endl;
	}
protected:
private:
	int a ;
	char *p;
};

//给对象搭建一个舞台,研究对象的行为
void objplay()
{
	//先创建的对象 后释放
	Test t1;
	t1.print();

	printf("分隔符\n");
	Test t2;
	t2.print();
}
void main11()
{
	objplay();
	cout<<"hello..."<<endl;
	system("pause");
	return ;
}


构造函数的分类及调用

1无参数构造函数

调用方法:Test t1, t2;


2有参构造函数
//有参数构造函数的三种调用方法
class Test5
{
private:
	int a;
public:
	//带参数的构造函数
	Test5(int a)
	{
		printf("\na:%d", a);
	}
	Test5(int a, int b)
	{
		printf("\na:%d b:%d", a, b);
	}
public:
};

int main55()
{
	Test5 t1(10);  //c++编译器默认调用有参构造函数 括号法 
	Test5 t2 = (20, 10); //c++编译器默认调用有参构造函数 等号法
	Test5 t3 = Test5(30); //程序员手工调用构造函数 产生了一个对象 直接调用构造构造函数法

	system("pause");
	return 0;
}

3.拷贝构造函数调用时机
#include "iostream"
using namespace std;

class AA
{
public:
	AA() //无参构造函数 默认构造函数
	{	
		cout<<"我是构造函数,自动被调用了"<<endl;
	}
	AA(int _a) //无参构造函数 默认构造函数
	{	
		a = _a;
	}
 	AA(const AA &obj2)
 	{
 		cout<<"我也是构造函数,我是通过另外一个对象obj2,来初始化我自己"<<endl;
 		a = obj2.a + 10;
 	}
	~AA()
	{
		cout<<"我是析构函数,自动被调用了"<<endl;
	}
	void getA()
	{
		printf("a:%d \n", a);
	}
protected:
private:
	int a;
};
//单独搭建一个舞台
void ObjPlay01()
{
	AA a1; //变量定义
	
	//赋值构造函数的第一个应用场景 
	//用对象1 初始化 对象2                                                    调用a2的copy构造函数
	AA a2 = a1; //定义变量并初始化 //初始化法

	a2 = a1; //用a1来=号给a2 编译器给我们提供的浅copy
}

第二个应用场景
//单独搭建一个舞台
void ObjPlay02()
{
	AA a1(10); //变量定义

	//赋值构造函数发表文章的第一个应用场景
	//用对象1 初始化 对象2 
	AA a2(a1); //定义变量并初始化 //括号法

	//a2 = a1; //用a1来=号给a2 编译器给我们提供的浅copy
	a2.getA();
} 
//注意:初始化操作 和 等号操作 是两个不同的概念

第3个调用场景
#include<iostream>
using namespace std;

class Location
{
public:
	Location(int xx = 0, int yy = 0)
	{
		X = xx;
		Y = yy;
		cout << "Construct Object\n"; 
	}
	//copy构造函数 完成对象初始化
	Location(const Location &obj)
	{
		X = obj.X;
		Y = obj.Y;
	}

	~Location()
	{
		cout << X << "," << Y << "Object destroyed" << endl;
	}

	int GetX()
	{
		return X;
	}
	int GetY()
	{
		return Y;
	}


private:
	int X;
	int Y;

};

//业务函数  形参是一个元素 void f(Location p)
void f(Location p)
{ 
	cout << p.GetX() << endl; 
}
void playobj()
{
	Location  a(1, 2);
	Location  b = a;
	cout << "b对象已经初始化完毕" << endl;
	f(b); //b实参取初始化形参p,会调用copy构造函数
}

int main()
{
	playobj();
	system("pause");
	return 0;
}


第4个调用场景

#include<iostream>
using namespace std;

class Location
{
public:
	Location(int xx,int yy)
	{
		X = xx;
		Y = yy;
	}
	Location(Location &obj)
	{
		X = obj.X;
		Y = obj.Y;
	}

private:
	int X;
	int Y;

};

//结论1 : 函数的返回值是一个元素 (复杂类型的), 
//返回的是一个新的匿名对象(所以会调用匿名对象类的copy构造函数)
Location g()//g函数 返回一个元素 
{
	Location A(1, 2);
	return A;
}


//结论2: 有关 匿名对象的去和留
//如果用匿名对象  初始化 另外一个同类型的对象, 匿名对象 转成有名对象
//如果用匿名对象  赋值给 另外一个同类型的对象, 匿名对象 被析构
void objplay2()
{
	g();
}

void objplay3()
{
	Location m = g();
	printf("匿名对象被扶正,不会被析构掉");
	
	Location m2(1,2);
	m2 = g();
	printf("匿名对象赋值给m2,匿名对象会被析构掉");
}

int main()
{
	system("pause");
	return 0;
}



默认构造函数

二个特殊的构造函数

1)默认无参构造函数

当类中没有定义构造函数时,编译器默认提供一个无参构造函数,并且其函数体为空

2)默认拷贝构造函数

当类中没有定义拷贝构造函数时,编译器默认提供一个默认拷贝构造函数,简单的进行成员变量的值复制




构造函数调用规则研究

1)当类中没有定义任何一个构造函数时,c++编译器会提供默认无参构造函数和默认拷贝构造函数

2)当类中定义了拷贝构造函数时,c++编译器不会提供无参数构造函数

3) 当类中定义了任意的非拷贝构造函数(即:当类中提供了有参构造函数或无参构造函数),c++编译器不会提供默认无参构造函数

4 )默认拷贝构造函数成员变量简单赋值

总结:只要你写了构造函数,那么你必须用。





构造函数的初始化列表

#include <iostream>
using namespace std;

class A
{
public:
	A(int _a)
	{
		a = _a;
		cout << "构造函数" << "a" << a << endl;
	}

	~A()
	{
		cout << "析构函数" << "a" << a << endl;
	}

protected:
private:
	int a;
};


//1 构造函数的初始化列表  解决: 在B类中 组合了一个 A类对象 (A类设计了构造函数)
//根据构造函数的调用规则 设计A的构造函数, 必须要用;没有机会初始化A
//新的语法  Constructor::Contructor() : m1(v1), m2(v1,v2), m3(v3)
class B
{
public:
	B(int _b1, int _b2) : a1(1), a2(2), c(0)
	{

	}

	B(int _b1, int _b2, int m, int n) : a1(m), a2(n), c(0)
	{
		b1 = _b1;
		b2 = _b2;
		cout <<"B的构造函数"<<endl;
	}
	~B()
	{
		cout<<"B的析构函数" <<endl;
	}

protected:
private:
	int b1;
	int b2;
	A a2;
	A a1;
	const int c;
};


//2 先执行 被组合对象的构造函数 
//如果组合对象有多个,按照定义顺序, 而不是按照初始化列表的顺序

//析构函数 : 和构造函数的调用顺序相反

//3 被组合对象的构造顺序 与定义顺序有关系 ,与初始化列表的顺序没有关系.
//4 初始化列表 用来 给const 属性赋值 
void obj10play()
{
	
	//A a1(10);
	//B ojbB(1, 2);

	//1参数传递 
	B ojbB2(1, 2,3, 4);

	//2 调用顺序
	
	return ;
}

void main100()
{
	obj10play();
	system("pause");
}


构造中调用构造
#include "iostream"
using namespace std;


//构造中调用构造是危险的行为
class MyTest
{
public:
	MyTest(int a, int b, int c)
	{
		this->a = a;
		this->b = b;
		this->c = c;
	}

	MyTest(int a, int b)
	{
		this->a = a;
		this->b = b;

		MyTest(a, b, 100); //产生新的匿名对象                     //理解:当调用此构造函数时,会建立一个匿名对象,此对象被建立之后随着构造函数的结
	}                                                                 //      束而被析构掉,所以打印了析构函数的内容。此时,c所显示的就是乱码
	~MyTest()
	{
		printf("MyTest~:%d, %d, %d\n", a, b, c);
	}

protected:
private:
	int a;
	int b;
	int c;

public:
	int getC() const { return c; }
	void setC(int val) { c = val; }
};

int main()
{
	MyTest t1(1, 2);
	printf("c:%d", t1.getC()); //请问c的值是?   乱码
	system("pause");
	return 0;
}





















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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值