类和对象-对象特性(匿名对象与析构函数,深拷贝与浅拷贝,初始化列表,静态成员变量与函数,成员变量与成员函数分开存储,this指针的用途,空指针访问成员函数,const修饰的场函数与常对象等.....)

对象的初始化和清理

*生活中我们买的电子产品基本都会有出场设置,在某一天我们不用的时候也会删除一些自己信息数据安全

*C++中的面向对象来源于生活,每个对象也都会有初始设置以及对象销毁前的清理数据的设置。

构造函数和析构函数

对象的初始化和清理是两个非常重要的问题:

        一个对象或者变量没有初始状态,对齐使用后果是未知

        同样的使用完一个对象或变量,没有及时清理,也会造成一定的安全问题

C++利用了构造函数和析构函数解决上述问题,这两个函数将会被编译器自动调用,完成对象初始化和清理工作。对象的初始化和清理工作是编译器强制要我们做的事情,因此如果我们不提供构造和析构,编译器会提供编译器提供的构造函数和析构函数是空实现。

构造函数:主要作用在于创建对象时为对象的成员属性赋值,构造函数由编译器自动调用,无须手动调用。

析构函数:主要作用在于对象销毁前系统自动调用,执行一些清理工作

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

//构造函数:创建对象时为成员属性初始化
class Person
{
	// 1.构造函数
	// 函数名与类名形同
	// 没有void也没有返回值
	// 有参数,可以函数重载
	// 对象在被创建的时候会自动调用且只会调用一次
	// 若没有写构造函数,则系统会自动写构造函数,且默认为public
	//Person()
	//{
	//}

public:
	Person()
	{
		cout << "Person类构造函数调用" << endl;
	}


	//2.析构函数
	//没有返回值,没有void
	//函数名与类名相同
	//需要在函数名前加 ~
	//没有形参,所以也没有重载
	//对象在销毁前会自动调用这个析构函数,且只会调用一次
	//如果程序员没写析构函数,系统会自动写
	//~Person()
	//{
	//}

	~Person()
	{
		cout << "Person类析构函数调用" << endl;
	}
};

void test01()
{
	Person p;//在栈上的数据,在test01()函数执行完毕后,就会释放这个对象
}

int main()
{
	test01();

	Person p;//对象创建,自动调用构造函数

	system("pause");
	//在函数结束前会自动调用析构函数
	return 0;
}

构造函数的分类和调用

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

//构造函数的分类
//1.按照参数分类  无参构造 (默认构造)    有参构造
//2.按照类型分类  普通构造	拷贝构造(形参必须是引用形参,加const)
class Person
{
public:

	Person()
	{
		cout << "Person类无参构造函数的调用" << endl;
	}
	Person(int age)
	{
		cout << "Person类有参构造函数的调用" << endl;
		this->age = age;
	}
	//拷贝构造函数
	Person(const Person& p)
	{
		cout << "Person类拷贝构造函数的调用" << endl;
		this->age = p.age;
	}

	int age;

	~Person()
	{
		cout << "Person类析构函数的调用" << endl;
	}

};

//构造函数的调用
int main()
{
	//1. 括号法
	Person p1;		//Person类无参构造函数的调用	
	//注意默认构造函数调用 后面不要加()
	// 错误的:  Person p1();
	// 因为编译器会认为这是一个函数声明
	Person p2(10);	// Person类有参构造函数的调用	
	Person p3(p2);	// Person类拷贝构造函数的调用

	p1 = p2;
	cout << p1.age << endl;
	cout << p2.age << endl;
	cout << p3.age << endl;

	//2.显示法
	// 不知道为什么,在我的电脑上显示法用不了
    // 知道了,复制构造函数形参前面要加const
	//Person p4;
	//Person p5 = Person(10);
	//Person p6 = Person(p5);
	Person(10);//匿名对象,当前行执行结束后,系统会立即回收调匿名对象
	// 编译器会把Person(p3) ===== Person p3 创建变量
	cout << "aaaaaaa" << endl;

	//3.隐式转换法
	Person p7;
	Person p8 = 20;
	Person p9 = p8;
	cout << p8.age << endl;
	cout << p9.age << endl;


	system("pause");
	return 0;
}


拷贝构造函数的调用时机

1.使用一个已经创建完毕的对象来初始化一个对象

2.值传递的方式给函数参数传值

3.以值方式返回局部变量

2,3本质上就是第一种

第三种在本机上与老师讲述的不同,供参考,按理说地址应该时不一样的,在我的电脑上地址一直时一样的

#include<iostream>
using namespace std;

// 拷贝构造函数的调用时机
class Person
{
public:
	Person()
	{
		cout << "Person类默认构造函数的调用" << endl;
	}
	Person(int age)
	{
		this->age = age;
		cout << "Person类有参构造函数的调用" << endl;
	}
	Person(const Person& p)
	{
		this->age = p.age;
		cout << "Person类拷贝构造函数的调用" << endl;
	}

	int age;

	~Person()
	{
		cout << "Person类析构构造函数的调用" << endl;
	}

};

//1.使用一个已经创建的对象来初始化一个对象,将会调用拷贝构造函数
void test01()
{
	Person p1; // 默认函数调用
	Person p2 = p1;// 拷贝函数调用
}

//2.值传递的方式给函数参数传参
void work1(Person p) //值传递
{
	// 值传递相当于从从实参那里复制一份到该函数中的p
	// 拷贝函数调用
    // 相当于从调用函数中拷贝一份p到被调用的函数中
    // Person p(被调用的函数中的p) = p(调用函数中的p); 隐式转换法定义方式
}

void test02()
{
	Person p(10);// 有参函数调用
	work1(p);
}

//3.以值的方式返回局部变量
Person work2()
{
	Person p;//有参函数调用
	cout << (int)&p << endl;
	return p;
}
void test03()
{
	Person p = work2(); //类似于隐式转换法的定义方式
    // Person p1 = p2;
	cout << (int)&p << endl;
}
int main()
{
	//test01();
	//test02();
	test03();

	system("pause");
	return 0;
}

构造函数的调用规则

#define _CRT_SECURE_NO_WARNINGS 
#include<iostream>
using namespace std;
/*
	默认情况下,C++编译器至少给一个类添加三个函数
	1.默认构造函数(无参,函数体为空)
	2.默认析构函数(无参,函数体为空)
	3.默认拷贝构造函数,对属性进行值拷贝
*/
class Person
{
public:
	//Person()
	//{
	//	cout << "Person类无参构造函数的调用" << endl;
	//}
	//Person(int age)
	//{
	//	cout << "Person类有参构造函数的调用" << endl;
	//	this->age = age;
	//}
	Person(const Person& p)
	{
		this->age = p.age;
		cout << "Person类拷贝构造函数的调用" << endl;
	}

	int age;
	~Person()
	{
		cout << "Person类析构函数的调用" << endl;
	}
};
//构造函数调用规则如下
//1.
//如果用户定义了有参构造函数,C++不在提供默认构造参数,但会提供默认拷贝构造函数
//void test01()
//{
//	//Person p; 
//	Person p1(10);
//	Person p2(p1);
//	cout << p2.age << endl;
//}

//2
//如果用户定义了拷贝构造函数,C++不会提供其它构造函数
void test02()
{
	
}
int main()
{
	//test01();


	system("pause");
	return 0;
}

深拷贝与浅拷贝

深拷贝:在堆区重新申请空间,进行拷贝操作

浅拷贝:简单的赋值拷贝操作,比如类中的拷贝构造函数简单的赋值拷贝,编译器提供的拷贝构造函数都叫浅拷贝,本质也就是等号赋值操作

浅拷贝带来的问题就是堆区的内存重复释放

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

//深拷贝与浅拷贝
class Person
{
public:
	Person()
	{
		cout << "Person类默认构造函数的调用" << endl;
	}
	Person(int age,int height)
	{
		this->age = age;
        // 在堆区开辟的空间,需要程序员手动释放
		this->height = new int(height);
		cout << "Persn有参构造函数的调用" << endl;
	}
	int age;
	// 这是一个地址,存储地址,在类中并不会自动开辟地址,需要手动开辟地址(个人理解)
	int* height;

	~Person()
	{
		// 析构代码:将堆区开辟的数据做释放操作
		if (height != NULL)
		{
			delete height;// 释放空间
			height = NULL;
		}
		cout << "Person类析构函数的调用" << endl;
	}
};
void test01()
{
	Person p1(10,163);
	cout << "p1的年龄为:" << p1.age << "  p1的身高为:" << *(p1.height) <<  endl;

	Person p2(p1);
    // 在栈区,先进后出,后进先出,p2先被释放,所以p2先调用析构函数释放空间
    // 之后p1就无法在重复释放同一块空间了,没有权限,
    // 释放空间也可以理解为原先指向该空间的指针全部作废,成为自由无指向的空间(个人理解)
	cout << "p2的年龄为:" << p2.age << "  p2的身高为:" << *(p2.height) << endl;
}
int main()
{
	test01();

	system("pause");
	return 0;
}

浅拷贝的问题要用深拷贝来解决.

如果属性有在堆区开辟的,一定要自己提供拷贝构造函数,防止浅拷贝带来的问题

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

//深拷贝与浅拷贝
class Person
{
public:
	Person()
	{
		cout << "Person类默认构造函数的调用" << endl;
	}
	Person(int age,int height)
	{
		this->age = age;
		this->height = new int(height);
		cout << "Persn有参构造函数的调用" << endl;
	}

	// 自己实现拷贝构造函数来结局浅拷贝所带来的问题
	Person(const Person& p)
	{
		cout << "Person类拷贝构造函数的调用" << endl;
		this->age = p.age;
		// 编译器写的代码是:
		// this -> height = p.height;
		this->height = new int(*(p.height));// 开辟空间的同时也在赋值
	}

	int age;
	// 这是一个地址,存储地址,在类中并不会自动开辟地址,需要手动开辟地址(个人理解)
	int* height;

	~Person()
	{
		// 析构代码:将堆区开辟的数据做释放操作
		if (height != NULL)
		{
			delete height;
			height = NULL;
		}
		cout << "Person类析构函数的调用" << endl;
	}
};
void test01()
{
	Person p1(10,163);
	cout << "p1的年龄为:" << p1.age << "  p1的身高为:" << *(p1.height) <<  endl;

	Person p2(p1);
	cout << "p2的年龄为:" << p2.age << "  p2的身高为:" << *(p2.height) << endl;
}
int main()
{
	test01();

	system("pause");
	return 0;
}

初始化列表

主要用途: 对类中的属性进行初始化的操作

构造函数(): 属性1(值1),属性2(值2).....{};

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

class Person
{
public:

	//传统的初始化方式
	//Person(int a,int b, int c)
	//{
	//	this->a = a;
	//	this->b = b;
	//	this->c = c;
	//}

	// 低阶初始化列表,只能初始化一次
	Person() :a(10), b(20), c(30)
	{
	
	}

	// 高阶初始化列表
	// 括号外的a作用域是此函数体外的,括号内的作用域是此函数内的
	Person(int a, int b, int c) :a(a), b(b), c(c)
	{
	
	}


	int a;
	int b;
	int c;

};
void test01()
{
	//Person p(10, 20, 30);
	//Person p;
	Person p(30, 20, 10);
	cout << "a = " << p.a << endl;
	cout << "b = " << p.b << endl;
	cout << "c = " << p.c << endl;
}
int main()
{
	test01();

	system("pause");
	return 0;
}

类对象作为类成员

C++类中的成员可以是另一个类的对象,我们称该成员为 对象成员

当其它类对象作为本类对象,构造时先构造类对象,再构造自身。(现有肢体,再有整体)

析构的顺序与构造的顺序相反,(人性化:先杀人,再解肢

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

// 类对象作为类成员
class Phone
{
public:
	string pName;

	Phone(string name) :pName(name)
	{
		cout << "Phone类有参构造函数的调用" << endl;
	}

	~Phone()
	{
		cout << "Phone类析构函数的调用" << endl;
	}
};
class Person
{
public:
	string name;
	Phone phone;

	// phone(pName) 相当于:
	// Phone phone = pName    相当于 Phone phone(pName);
	// 对象的创建:隐式转换法
	Person(string name, string pName) :name(name), phone(pName)
	{
		cout << "Person类有参构造函数的调用" << endl;
	}

	~Person()
	{
		cout << "Person类析构函数的调用" << endl;
	}
};
void test01()
{
	/*
		Phone类有参构造函数的调用
		Person类有参构造函数的调用
		张三拿着三星
		Person类析构函数的调用
		Phone类析构函数的调用
	*/
	Person p("张三", "三星");
	cout << p.name << "拿着" << p.phone.pName << endl;
}
int main()
{
	test01();

	system("pause");
	return 0;
}

静态成员

类中含有成员属性和成员函数

静态成员变量

1.所有对象共享同一份数据

2.在编译阶段分配内存

3.类内声明,类外初始化

4.静态成员变量也是有访问权限的,private类型类外不可以访问,但是可以类外初始化操作

静态成员变量不属于某个对象,是共有的,所有对象都共享同一份数据

因此静态成员变量有两种访问方式

1.通过对象进行访问

2.通过类名进行访问

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

//静态成员变量
//1. 所有对象共享同一份数据
//2. 在编译阶段分配内存
//3. 类内声明,类外初始化

static int b;

class Person//相当于定义了一个类型
{
public:
	static int a;
private:
	static int b;
};
// :: 代表作用域
int Person::a = 10;
int Person::b = 20;

void test01()
{
	Person p1;
	cout << p1.a << endl;

	Person p2;
	p2.a = 20;
	cout << p1.a << endl;

}
void test02()
{
	//静态成员变量不属于某个对象,是共有的,所有对象都共享同一份数据

	//	因此静态成员变量有两种访问方式

	//	1.通过对象进行访问
	Person p;
	cout << p.a << endl;

	//	2.通过类名进行访问
	cout << Person::a << endl;
	//4.静态成员变量也是有访问权限的,private类型类外不可以访问,但是可以类外初始化操作
	//cout << Person::b << endl;
}

int main()
{
	//test01();
	test02();

	system("pause");
	return 0;
}

静态成员函数

1.所有对象共享同一个函数

2.静态成员函数只能访问静态成员变量

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

//静态成员函数
class Person
{
public:
	static void func()
	{
		a = 200; // 静态成员函数可以访问静态成员变量

		// 因为每个对象共享这个静态成员函数,而非静态成员变量属于某个特定的对象的
		// 故函数体无法区分给哪个对象的b赋值
		// b = 10; // 静态成员函数不可以访问非静态成员变量
		cout << "static void func()函数的调用" << endl;
	}

	static int a;// 静态成员变量
	int b;// 非静态成员变量

private:
	//静态成员函数也是有访问权限的
	static void func2()
	{
		cout << "static void func2()的调用" << endl;
	}
};
int Person::a = 0;

//如何访问静态成员函数
void test01()
{
	// 1.通过创建对象来访问
	Person p;
	p.func();

	// 2.通过类名来访问
	// 通过类名就可以访问就说明它不属于某一个对象,每一个对象都在共享同一个静态成员函数,
	// 所以不需要创建对象就可以访问这个静态成员函数
	Person::func();

	//Person::func2(); // 类外访问不到私有的静态成员函数

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

C++对象模型和this指针

成员变量和成员函数分开存储

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


// 1.只有非静态成员变量才属于类的对象上
// 2.空对象占用内存1字节
class Person
{
	int a;//非静态成员变量是属于类的对象上的

	static int b;//静态成员变量不属于类的对象上

	void func1() //非静态成员函数不属于类的对象上
	{
	
	}

	static void func2()//静态成员函数不属于类的对象上
	{
	
	}
};
int Person:: b = 0;
void test01()
{
	Person p;
	// 空对象占用的内存空间为1
	// C++编译器会给每个空对象分配一个字节空间,是为了区分空对象在内存中的位置
	// 每个空对象也应该有一个独一味二的内存地址
	cout << sizeof(p) << endl;
}
void test02()
{
	Person p;
	cout << sizeof(p) << endl;
}
int main()
{
	//test01();
	test02();

	system("pause");
	return 0;
}

this指针的用途

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

// 1.解决名称冲突
// 2.返回对象本身 return *this;

class Person
{
public:
	int age;
	Person(int age)
	{
		// this指针指向 被调用的成员函数 所属的对象
		this->age = age;
	}


	//注意值传递和址传递和引用传递,如果将&去掉,结果将不同
	Person& personAddAge(Person& p)
	{
		this->age += p.age;
		return *this;
	}
};

void test01()
{
	// p被创建,同时调用有参构造函数(成员函数)
	// 该成员函数属于p,故this指向p
	Person p(18);

	cout << p.age << endl;
}
void test02()
{
	Person p1(10);
	Person p2(10);

	// 链式编程思想
	p2.personAddAge(p1).personAddAge(p1).personAddAge(p1).personAddAge(p1);
	cout << p2.age << endl;
}
int main()
{
	//test01();

	test02();
	system("pause");
	return 0;
}

空指针访问成员函数

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

class Person
{
public:
	void showClassName()
	{
		cout << "这是一个Person类" << endl;
	}

	void showClassAge()
	{
		// 防止代码出错
		// 提高代码的健壮性
		if (this == NULL)
		{
			return;
		}

		// 在属性前面都默认加了 this-> ,告诉你这是当前对象的属性
		// 但是此时this指针指向p , p是一个空指针 ,没有指向一个确切的对象
		// 故出错
		cout << "age = " << m_Age << endl;
	}

	int m_Age;


};
void test01()
{
	Person* p = NULL;
	p->showClassName();

	p->showClassAge();
}
int main()
{
	test01();

	system("pause");
	return 0;
}

const修饰成员函数(常函数与常对象 )

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

// 1.常函数
class Person
{
public:
	// this的本质是指针常量 Person * const this 指针的指向是不可以被修改的
	// 在成员函数后面加const ,目的是让指针指向的值也不可以被修改,const Person * const this
	void showPerson() const // 这个函数称为常函数
	{
		//this->a = 100;
		this->b = 20;//因为b有关键字mutable修饰  mutable(可变的)
	}
	void func()
	{
		a = 20;
	}

	int a;
	mutable int b;
};
void test01()
{
	Person p;
	p.showPerson();
	cout << p.b << endl;
}

//2.常对象
// 常对象只能调用常函数
void test02()
{
	const Person p;
	//p.a = 10;
	p.b = 100;// 因为b被mutable(可变的)关键词修饰,所以在常对象下也可以被修改
	cout << p.b << endl;

	p.showPerson();//常对象只能调用常函数
	cout << p.b << endl;

	//因为在普通(非静态,非常量)的成员函数是可以修改成员属性的
	//而常对象不能修改其未被mutable修饰的成员属性的值
	//故常对象不可以调用普通成员函数(成员函数不属于对象本身)
	//p.func();
}
int main()
{
	//test01();
	test02();
	system("pause");
	return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值