c++学习第五讲---类和对象---封装,对象的初始化和清理

对象:万事万物皆为对象;

类:具有相同性质的对象,可以抽象为类。

1.封装:

1.封装的意义:

(1)将属性和行为作为一个整体,表现生活中的事物。

(2)将属性和行为加以权限控制。

注:叫法:

类中的属性和行为:统称成员

属性:又称为 成员属性、成员变量

行为:又称为 成员函数、成员方法

2.语法:

class 类名 { 访问权限 :属性 / 行为 } ;

实例化:通过一个类,创建出一个对象。

例:设计一个圆类,求圆的周长:

//设计一个圆类,求圆的周长
const double PI = 3.14;

class Circle
{
	//访问权限
public:
	//属性
	int r;
    //行为
	double C()
	{
		return 2 * PI * r;
	}
};
int main()
{
	Circle round;//实例化
	round.r = 10;
	cout << "圆的周长:" << round.C() << endl;
	return 0;
}

3.访问权限:

公共权限(public):类内可以访问,类外可以访问
保护权限(protected): 类内可以访问,类外不可以访问,子类可访问父类中保护内容
私有权限(private): 类内可以访问,类外不可以访问,子类不可访问父类中私有内容
(保护和私有在继承中有区别)

例:

class Person
{
public:
	string Name;
protected:
	string car;
private:
	int Password;
public:
	void func()
	{
		Name = "name";
		car = "car";
		Password = 1;//类内均可访问
	}
};
int main()
{
	Person p1;
	p1.Name = "zhangsan";
	p1.car = "benchi";//报错,保护和私有访问不到
	return 0;
}

4.struct和class的区别:

基本一致,但默认的访问权限不同。

struct的默认权限为公共,而class的默认权限为私有。
(默认权限,即未标明访问权限时,创建成员时的成员权限)

5.成员属性设为私有:

将成员属性设为私有,提供公共的函数来读写私有成员。

优点:
1.可以自己控制读写权限;
2.对于写,可以检测数据有效性

//人类
class Person
{
public:
	//设置姓名
	void setName(string name)
	{
		m_Name = name;
	}
	//获取姓名
	string getName()
	{
		return m_Name;
	}
	//获取年龄
	int getAge()
	{
		return m_Age;
	}
	//设置偶像
	void setIdol(string Idol)
	{
		m_Idol = Idol;
	}
	//设置年龄
	void setAge(int Age)
	{
		if (Age < 0 || Age>150)
		{
			cout << "年龄有误,写入失败" << endl;
			return;
		}
		m_Age = Age;
	}
private:
	string m_Name;//姓名 可读可写
	int m_Age = 18;//年龄 可读  也可以写但只限制在0到100(检测数据有效性)
	string m_Idol;//偶像 只写
};

2.对象的初始化和清理:

1.构造函数和析构函数:

构造函数:为对象的成员属性赋值。

析构函数:对象销毁前进行清理。

c++利用这两个函数进行对象的初始化和清理,必须有,如果程序员不实现,会由编译器自动实现,但是是空实现。(即函数中没有内容)

注:构造函数和析构函数应放在public里。

(1)构造函数:类名(){ }

1.没有返回值也不写void;
2.函数名称与类名相同;
3.构造函数可以有参数,因此可以重载;
4.构造函数会由编译器自动调用,无需手动调用。

(2)析构函数:~类名(){ }

1.没有返回值也不写void;
2.函数名称与类名相同,前加 ~;
3.构造函数不可以有参数,因此不可以重载;
4.构造函数会由编译器自动调用,无需手动调用。

2.构造函数的分类以及调用:

两种分类方式:
按参数分类:有参构造和无参构造
按类型分类:普通构造和拷贝构造

三种调用方式:
括号法,显示法,隐式转换法。

1.有参构造和无参构造:
	Person()
	{
		cout << "构造函数的调用" << endl;
	}	
	Person(int a)
	{
		cout << "构造函数的调用" << endl;
	}
2.拷贝构造函数:

传入一个对应类的引用,同时必须加const。

	Person(const Person &p)
	{
		age = p.age;
	}
3.调用:括号法:

用括号传参的方式决定用哪一种构造函数。

	//括号法
	Person p1;//默认无参构造
	Person p2(10);//有参构造
	Person p3(p2);//引用构造

注:用无参构造时不要加(),编译器会默认为函数声明,而非创建对象。

4.调用:显示法:
	//显示法
	Person p1;
	Person p2 = Person(10);//有参构造
	Person p3 = Person(p2);//拷贝构造

注:1.匿名对象, 会在本行执行后就被回收掉。

Person(10);

2.不要利用拷贝构造函数初始化匿名对象,因为会被认为是一个对象的声明,重定义报错。

Person(p3);
5.调用:隐式转换法:
	//隐式转换法
	Person p4 = 10;
	Person p5 = p4;

   3.拷贝构造函数的调用时机:                            

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

void test01()
{
	Person p1(10);
	Person p2(p1);
}

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

void work(Person p)//拷贝构造
{

}
void test02()
{
	Person p;
	work(p);
}

4.构造函数的调用规则:

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

(1)默认构造函数(无参,函数体为空)
(2)默认析构函数(无参,函数体为空)
(3)默认拷贝构造函数,对属性进行值拷贝(即不写拷贝构造函数也有默认的)

2.如果写了有参构造函数,编译器就不再提供默认构造函数,但依然有拷贝构造。
  如果写了拷贝构造函数,编译器就不提供其他普通构造函数了。

无参 < 有参 < 拷贝

class Person
{
public:	
	Person(int a)
	{
		cout << "Person有参构造函数调用" << endl;
	}		
	~Person()
	{
		cout << "Person析构函数调用" << endl;
	}
	int m_age;
};
int main()
{
	Person p;//报错,不再有默认构造函数
	return 0;
}

5.深拷贝与浅拷贝:

浅拷贝:纯赋值拷贝
深拷贝:在堆区重新申请空间,进行拷贝操作。

注:默认拷贝构造函数为浅拷贝,会出现如下问题。

class Person
{
public:	
	Person(int a,int b)
	{
		m_age = a;
		m_height = new int(b);
		cout << "Person有参构造函数调用" << endl;
	}		
	~Person()
	{
		if (m_height != NULL)
		{
			delete m_height;
			m_height = NULL;
		}
		cout << "Person析构函数调用" << endl;
	}
	int m_age;
	int* m_height;
};
int main()
{
	Person p1(18, 160);
	Person p2(p1);//此处使用默认拷贝构造,为浅拷贝,将p1.m_height的地址直接拷贝给p2,后来析构时就delete两次了
	return 0;
}

解决办法:自己写一个深拷贝构造函数:

	Person(const Person& p)
	{
		m_age = p.m_age;
		m_height = new int(*p.m_height);
	}

6.初始化列表:

用途:初始化属性。

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

class Person
{
public:
	Person(int a,int b) :A(a), B(b) {
	}
	int A;
	int B;
};

7.类对象作为类成员:

类中的成员可以是另一个类的对象,该成员叫做对象成员。

class A{};
class B {
	A a;
};

注:

1.构造时,先构造内对象,然后外对象。
   析构时,先析构外对象,再析构内对象。

2.在初始化列表给对象成员的属性赋值时,可以直接用属性对应的数据类型赋值
(不好说明,请看案例):

class Person
{
public:
	Person(int a)
	{
		age = a;
	}
	int age;
	//string name;
};
class class1
{
public:
	int personcount;
	Person p;
	class1(int cou, int age) :personcount(cou), p(age){}//注意这里
};
int main()
{
	class1 c(50, 18);
	cout << c.personcount << c.p.age << endl;
	return 0;
}

注意Person类型的 p 用 int 类型的age初始化,相当于 Person p = age ,是隐式转换法,但是内对象的类需要有有参构造函数。

8.静态成员:

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

(1)静态成员变量:

1.所有对象共享同一份数据(同一块地址,一个修改另一个也改)
2.在编译阶段分配内存
3.类内声明,类外初始化(要在函数之外初始化)

int Person::A = 100;
int main() {
	Person p;
	cout << p.A << endl;
	return 0;
}

注:

1.静态成员变量有两种访问方式:对象,类名

	Person p;
	cout << p.A << endl;//对象
	cout << Person::A << endl;//类名

2.类外无法访问私有静态成员变量,但也可在函数之外初始化。

(2)静态成员函数:

1.所有对象共享同一个函数
2.静态成员函数只能访问静态成员变量

class Person
{
public:
	static void func()
	{
		cout << "static void func调用" << endl;
		a = 100;
		b = 200;//报错,无法访问
	}
	static int a;
	int b;
};

注:

1.两种访问方式:对象,类名

	Person p;
	p.func();
	Person::func();

你可能感兴趣的:(学习)