c++成员属性设置为私有

126 阅读3分钟

c++成员操作

优点:将所有成员设置为私有,可以自己控制读写权限

优点2:对于写权限,我们可以检测数据的有效性

#include<iostream>
#include<string>
using namespace std;
class Prosenal {
private:
	string name;
	int age;
	string sex;
public:
	//类设置私有好处之一,便于控制类的读写操作。
	string getName()
	{
		return name;
	}
	void setName(string namea)
	{
		name = namea;
	}
	int getAge() 
	{
		age = 0;
		return age;
	}
};
int main()
{
	Prosenal p;
	p.setName("佳佳");
	cout << "名字是:" << p.getName() << endl;
	cout << "年龄" << p.getAge() << endl;
	system("pause");
	return 0;
}

案例1:设计一个立方体类

#include<iostream>
using namespace std;
class Cude 
{
private:
	int m_h;
	int m_w;
	int m_l;
public:
	void setH(int h) 
	{
		m_h = h;
	}
	void setw(int w)
	{
		m_w = w;
	}
	void setl(int l)
	{
		m_l = l;
	}
	int getH()
	{
		return m_h;
	}
	int getw()
	{
		return m_w;
	}
	int getl()
	{
		return m_l;
	}
	int ca_C()
	{
		return 2 * m_l * m_w + 2 * m_w * m_h + 2 * m_h * m_l;
	}
	int ca_v()
	{
		return  m_l * m_w * m_h;
	}
};

bool iscude(Cude &c1,Cude &c2) 
{
	if (c1.getH() == c2.getH() && c1.getw() == c2.getw() && c1.getl() == c2.getl()) {
		return true;
	}
	else {
		return false;
	}
}
int main()
{
	Cude c1;
	c1.setH(10);
	c1.setw(10);
	c1.setl(10);
	cout << "面积是:" << c1.ca_C()<<endl;
	cout << "体积是:" << c1.ca_v() << endl;
	Cude c2;
	c2.setH(10);
	c2.setw(10);
	c2.setl(11);
	bool ref = iscude(c1, c2);
	if (ref) 
	{
		cout << "他们相等" << endl;
	}
	else {
		cout << "他们不相等" << endl;
	}
	system("pause");
	return 0;
}

对象初始化和清理

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

构造函数和析构函数

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

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

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

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

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

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

构造函数语法

类名(){}

1.构造函数,没有返回值也不需要写void

2.构造函数与类名相同

3.构造函数可以有参数,因此可以发生函数重载

4.程序在调用对象时候会自动调用构造函数,无需手动调用,而且只会调用一次

析构函数语法

~类名(){}

1.析构函数,没有返回值也不需要写void

2.析构函数与类名相同,在名称前加上符号~

3.析构函数不可以有参数,因此不可以发生函数重载

4.程序在对象销毁前会自动调用析构,无需手动调用,而且只会调用一次

#include<iostream>
using namespace std;
class Preson {
	//构造函数几个注意点
	//1.没有返回值,不用写void
	//2.构造函数和类名相同
	//3.可以有参数,可以发生函数重载
	//4.初始化前自动调用,无需手动调用,而且只会调用一次
public:
	Preson()
	{
		cout << "构造函数被调用了" << endl;
	}
	//析构函数的几个注意事项
	//1.没有返回值,不需要写void
	//2.析构函数与类名相同
	//3.没有参数,不可以发生函数重载
	//4.对象销毁前自动调用,无需手动调用,而且只会调用一次
	~Preson()
	{
		cout << "析构函数被调用了" << endl;
	}
};
void test()
{
	Preson p;
}
int main()
{
	//test();//构造和析构函数都调用了。
	Preson p;//在输出面板中可以看到只调用了构造函数,析构并没有调用,是因为,system("pause")这一行代码。当我们按任意键,可以看到析构函数的调用
	system("pause");
	return 0;
}

构造函数的分类及调用

两种分类方式

按参数分:有参构造和无参构造

按类型分为:普通构造和拷贝构造

三种调用方式:

括号法,当调用无参构造时,不要加小括号,要不然会创建不出来,原因是:编译器会把调用无参构成的语句认为成函数的声明

显示法

隐式转化法

#include<iostream>
using namespace std;
class Person2 {
public:
	//构造函数的分类
	//按参数分:有参构造和无参构造
	//按构造类型分类:普通构造和拷贝构造
	//无参构造(默认构造)
	Person2()
	{
		cout << "无参构造函数调用" << endl;
	}

	Person2(int a)
	{
		cout << "有参构造函数调用" << endl;
		age = a;
	}
	//一个简单拷贝构造,将另一个实例中的数据拷贝给调用类本身
	Person2(const Person2& p)
	{
		age = p.age;
		cout << "拷贝构造函数调用" << endl;
	}
	~Person2()
	{
		cout << "析构函数调用" << endl;
	}
	int age;

};
void test1()
{
	//调用方式:
	//1.括号法
	//Person2 p;
	//Person2 p1();//不能这样调用无参构造函数,编译器会认为这样是在声明函数
	//Person2 p2(10);//构造函数重载,调用的是有参构造
	//cout << "年龄:" << p2.age << endl;//10
	//拷贝函数调用
	//Person2 p3(p2);
	//cout << "年龄:" << p3.age << endl;//10
	//2.显示法
	//Person2 p;
	//Person2 p1 = Person2(10);//有参构造调用
	//Person2 p2 = Person2(p1);
	//cout << "年龄:" << p1.age << endl;//10
	//cout << "年龄:" << p2.age << endl;//10
	//Person2(10);//这个叫做匿名对象,特点,他在执行完之后系统会立即回收匿名对象
	//cout << 111 << endl;
	//输出顺序
	//有参构造函数调用
	//析构函数调用
		//111
	//不要用匿名对象使用拷贝构造函数,编译器会认为Person2(p2) === Person p2 ,认为是对象声明
	//Person2(p5);可以用他实例新对象,前提是不能为前提实例的列如Person2(p2),p2前面就已经实例了,后面就不能在进行实例了
	//p5.age = 100;
	//cout << p5.age << endl;
	//Person2(p2);//前面已经实例,编译器发生错误,p2重定义

	//3.隐式转换法调用
	Person2 p1 = 10;//他相当于Person p1 = Person2(10)

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

拷贝构造函数调用时机

c++中拷贝构造函数调用时机通常有三种情况

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

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

以值方式返回局部对象

构造函数调用规则

默认情况下,c++编译器至少给一个类添加3个函数

1.默认构造函数(无参,函数体为空)

2.默认析构函数(无参,函数体为空)

3.默认拷贝构造函数,对属性进行值拷贝

构造函数调用规则如下

如果用户定义有参构造函数,c++不在提供默认无参构造函数,但是会提供默认拷贝构造

如果用户定义拷贝构造函数,c++不 会在提供其他构造函数

#include<iostream>
using namespace std;
class Person3 {
public:
	//c++编译器给一个类中添加至少三个函数:默认构造函数,析构函数,拷贝构造函数
	//如果我们给类自定义了个有参构造函数,那么,c++不会提供默认构造函数,但会给我们提供拷贝构造函数
	//Person3()
	//{
	//	cout << "无参构造函数调用了" << endl;
	//}
	//Person3(int A)//如果我们只写了有参构造函数,这时要注意,默认构造函数已经没有了,拷贝还在
	//{
	//	cout << "有参构造函数调用了" << endl;
	//	m_Age = A;
	//}
	Person3(const Person3 &p)
	{
		m_Age = p.m_Age;
	}
	~Person3()
	{
		cout<<"析构函数调用了" << endl;
	}
	int	m_Age;
};
//void test5()
//{
	//Person3 p;//默认构造已经没有了
	//Person3 p1(10);
	//Person3 p2(p1);//拷贝c++还会提供
	//cout << p1.m_Age << endl;
	//cout << p2.m_Age << endl;
//}
void test6() 
{
	//当我们只写了拷贝构造函数,这时其他函数都不会存在
	//Person3 p1;//报错
	//Person3 p1(10);//报错

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

深拷贝和浅拷贝

浅拷贝:简单的赋值拷贝操作

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

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

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

#include<iostream>
using namespace std;
class Person
{
public:
	//Person()
	//{
	//	cout << "无参构造函数调用" << endl;
	//}
	Person(int age, int Height)
	{
		m_age = age;
		m_Height = new int(Height);
		cout << "有参构造函数调用" << endl;
	}
	Person(const Person &p)
	{
		m_age = p.m_age;
		m_Height = new int(*p.m_Height);
		cout << "拷贝构造函数调用" << endl;
	}
	~Person()
	{
		if (m_Height != NULL) {
			delete m_Height;
			m_Height = NULL;
		}
		cout << "析构函数调用" << endl;
	}
	int m_age;
	int* m_Height;
};
void test()
{
	//Person p;
	Person p1(10, 180);
	Person p2(p1);//编译器会默认提供一个浅拷贝的构造函数,本质就是,简单的赋值操作
	cout << "年龄" << p1.m_age << endl;
	cout << "身高" << *p1.m_Height << endl;
	cout << "年龄" << p2.m_age << endl;
}
int main()
{
	test();
	system("pause");
	return 0;
}

初始化列表

作用:用于初始化属性

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

#include<iostream>
using namespace std;
class Person {
public:
	//Person(int a, int b, int c)//传统赋初值方法
	//{
	//	m_A = a;
	//	m_B = b;
	//	m_C = c;
	//}
	//利用初始化列表赋初值
	Person(int a, int b, int c) :m_A(a), m_B(b), m_C(c) 
	{
		cout << "列表初始化方法赋初值" << endl;
	}
	int m_A;
	int m_B;
	int m_C;
};
void test1()
{
	//Person p(10, 20, 30);
	//cout << "m_A=" << p.m_A << endl;
	//cout << "m_B=" << p.m_B << endl;
	//cout << "m_C=" << p.m_C << endl;
	Person p1(10, 20, 30);
	cout << "m_A=" << p1.m_A << endl;
	cout << "m_B=" << p1.m_B << endl;
	cout << "m_C=" << p1.m_C << endl;
}
int main()
{
	test1();
	system("pause");
	return 0;
}

类对象作为类成员

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

#include<iostream>
#include<string>
using namespace std;
class Phone 
{
public:
	Phone(string phoneName) :phoneName(phoneName)
	{
		cout << "Phone初始化列表构造函数调用" << endl;
	}
	~Phone()
	{
		cout << "Phone析构函数调用" << endl;
	}
	string phoneName;
};
class Person
{
public:
	string name;
	Phone myPhone;//这个是Phone类
	Person(string name, string phone):name(name), myPhone(phone){
		cout << "Person初始化列表构造函数调用" << endl;
	}
	~Person()
	{
		cout << "Person析构函数调用" << endl;
	}
	void playInfo()
	{
		cout << name << "手机" <<myPhone.phoneName << endl;
	}
};
void test01()
{
	Person p("佳佳", "苹果十三");
	p.playInfo();
}
int main()
{
	test01();
	system("pause");
	return 0;

静态成员

静态成员就是在成员变量和成员函数前面加上关键字static,称为静态成员

静态成员分为

静态成员变量

所有对象共享同一份数据

在编译阶段分配内存

类内声明,类外初始化

静态成员变量可以通过对象进行访问

对象名.静态成员变量

还可以通过类名进行访问

类名::静态变量名

静态成员变量也是有访问权限的

#include<iostream>
using namespace std;
class Person
{
	//静态成员变量
	//编译阶段分配内存,类内声明,类外初始化,所有对象共享静态成员变量
public:
	static int m_A;//类内声明
private:
	static int  m_B;//静态成员变量也是有权限的
};
//类外初始化
int Person::m_A = 100;
int Person::m_B = 200;
void test10()
{
	Person p;
	//通过对象进行访问静态成员变量
	cout << p.m_A << endl;//100
	//通过类名进行访问静态成员变量
	cout << Person::m_A << endl;//100
}
void test110()
{
	Person p;
	//cout << p.m_B << endl;//因为m_B权限是私有的,外部并不能访问
}
int main()
{
	//test10();
	test110();
	system("pause");
	return 0;
}

静态成员函数

所有对象共享同一个函数

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

两种调用方式

通过对象访问到静态成员函数

通过类名访问到静态成员函数

静态成员函数也是有访问权限的

#include<iostream>
using namespace std;
class Person
{
public:
	//静态成员函数
	//每个对象身上都会有一份
	//静态成员函数只能访问静态成员变量,不能访问,其他成员变量或者函数,原因,当每次构造对象的时候,都会实例一个新的对象,静态成员函数不知道用那个对象身上的成员变量
	static void fn()
	{
		cout << "我是静态成员函数" << m_C << endl;
		//cout << "我是静态成员函数" <<m_D<< endl;//在静态成员函数内只能访问静态成员变量。不能访问非静态成员
	}
	static int m_C;
	int m_D;
private:
	static void fn1()//静态成员函数也有访问权限
	{
		cout << "我是静态成员函数1" << m_C << endl;
	}
};
int Person::m_C = 100;
void test09()
{
	Person p;
	//第一种访问静态成员函数方式,通过对象方法
	p.fn();
	//第二种通过类名访问静态成员函数
	Person::fn();
}
int main()
{
	test09();
	system("pause");
	return 0;
}

c++对象模型和this指针

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

在c++中。类内的成员变量和成员函数分开存储

只有非静态成员变量才属于类的对象上

#include<iostream>
using namespace std;
class Person
{
public:
	int m_a; //非静态成员变量,属于类上
	int m_c;//非静态成员变量,属于类上
	float m_d;//非静态成员变量,属于类上
	static int m_b;//静态成员变量,不属于类上
	void fn()//非静态成员函数,但不属于类上
	{
		cout << "hello world" << endl;
	}
	static void fn1()//静态成员函数,但不属于类上
	{
		cout << "hello world" << endl;
	}
};
int Person::m_b = 100;
void test08()
{
	Person p;
	//cout << "sizeof = " << sizeof(p) << endl;//如果是一个空类的话,他的内存空间为1个字节
	cout << "sizeof = " << sizeof(p) << endl;//只有非静态成员变量才属于类的对象上,内存占用大小,非静态成员之间相加
}
int main()
{
	test08();
	system("pause");
	return 0;
}

this指针概念

通过上文可知,成员变量和成员函数是分开存储的,每一行非静态成员函数只诞生一份函数实例,也就是说多个同类型的对象会共用一块代码

那么问题是:这一块代码是如何区分那个对象调用自己的呢?

c++通过提供特殊的对象指针,this指针解决上述问题,this指针指向被调用的成员函数所属的对象

this指针是隐含每一个非静态成员函数内的一中指针

this指针不需要定义,直接使用即可

this指针的用途:

当形参和成员变量同名时,可用this指针来区分

在类的非静态成员函数中返回对象本身,可使用return *this

#include<iostream>
using namespace std;
class Person
{
public:
	int m_Age;
	int m_Height;
	Person(int age,int height)
	{
		//关于c++this指向问题,this指针指向被调用的成员函数所属的对象,通俗来说谁调用就指向谁,
		//this 只能用在类的内部,通过 this 可以访问类的所有成员,包括 private、protected、public 属性的。
		this->m_Age = age;//防止非静态成员变量重名。
		this->m_Height = height;
		this->fn();//在初始化变量同时,通过this调用fn函数
		//this->m_A = 1000;
	}
	void fn()
	{
		this->m_A = 10000;
		this->m_Height = 1500;
		this->fn1();
		cout << "hello world" << endl;
	}
	static void fn1()
	{
		cout << "hello world1" << endl;
	}
	static int m_A;
};
int main()
{
	Person p(10,100);
	cout << p.m_Age <<"\t" << p.m_Height << endl;
	cout << p.m_A << endl;
	system("pause");
	return 0;
}