c++类和对象

90 阅读11分钟

面向对象三大特性:封装、继承、多态

1.封装

1.1封装的意义

1.将属性和行为作为一个整体,表现生活中的事物 语法:calss 类名{ 访问权限:属性 / 行为};

示例

#include<iostream>
#include<string>
using namespace std;
const double PI = 3.14; //定义圆周率

class Circle //class代表设计一个类,类后面紧跟着的就是类名称
{
public://访问权限中的公共权限

	int m_r;//属性:定义圆的半径

	double perimeter() //行为:获取圆的周长
	{

		return 2 * PI * m_r;
	}
};

int main() {
	//实例化,通过一个类创建一个对象的过程
	//(通过圆类,创建具体圆的对象:周长)
	Circle c1;
	c1.m_r = 10;//给圆对象的属性进行赋值
	cout << "c1周长为: " << c1.perimeter() << endl;
	return 0;
}

2.将属性和行为加以权限控制 三种访问权限: public 公共权限 类内可以访问,类外可以访问 protected 保护权限 类内可以访问,类外不可以访问 pirvate 私有权限 类内可以访问,类外不可以访问

在类内函数func中,函数可以访问公共权限的m_Name,保护权限的m_car,私有权限的m_password {632444F9-CE0C-414F-9101-4F42AB0F9A55}.png

而在类外函数main中,函数只能访问公共权限的m_Name

屏幕截图 2024-10-26 110251.png

1.2 struct和class的区别

class作为类其默认的访问权限为私有,struct作为类时其默认访问权限为公有

屏幕截图 2024-10-26 111840.png

屏幕截图 2024-10-26 111920.png

1.3 成员属性设置为私有

1.将所以成员属性设置为私有,可以自己控制读写权限

#include<iostream>
#include<string>
using namespace std;
   class Li
{
public:
	   void setName(string Name)//设置名字可写
	   {
		   m_Name = Name;
		  
  }
	   string  getName() //设置名字可读
	   {
		   return m_Name;
	   }
	   string getcar() //设置汽车可读
	   {
		   return  m_car;
	   }
	   void setpassword(int password)
	   {
		  m_password= password ;//设置密码可写
       
	   }
private:
	string m_Name;//可读可写

	string m_car="奔驰"; //可读不可写

	string m_password;  //可写不可读

};
int main() {
	Li c1;
	c1.setName("wang");//名字设置
	cout << "名字为:" << c1.getName() << endl; //获取名字
	cout << "汽车为: " << c1.getcar() << endl;  //获取汽车
	c1.setpassword(123456);  //密码设置
	
	

	return 0;
}

2.对于写权限我们可以自己检测数据的有效性

#include<iostream>
#include<string>
using namespace std;
const double PI = 3.14;
   class Li
{
public:
	void setage(int age) {
		if (age<0 || age > 150)//对于写权限我们可以自己检测数据的有效性
                {
			cout << "您输入的年龄" << age << "有误,请更正!" << endl;
			return;
		}
		m_age=age;
	   }
	int getage() {
		return m_age;
	}
private:

	int  m_age=0; 	
};
int main() {
	Li c1;
	c1.setage(60);
	cout << "年龄为:" << c1.getage() <<"岁" << endl;
	
	

	return 0;
}

若输出值超过150或者小于0则会显示

{54A998CD-14CA-46F7-A884-DBAA90597232}.png

2.对象的初始化和清理

2.1构造函数和析构函数

构造函数进行初始化操作

语法:类名(){}

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

2.函数名称与类名相同

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

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

析构函数 进行清理操作

语法:~类名(){}

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

2.函数名称与类名相同在名称前加~

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

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

*构造和析构都必须有如果我们不提供,编译器自动提供一个空实现的构造和析构

#include<iostream>
#include<string>
using namespace std;
class person
{
public:
	person() {
		cout << "person的构造函数调用" << endl;
	}
	~person() {
		cout << "person的析构函数调用" << endl;
	}

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

2.2函数的分类以及调用

#include<iostream>
#include<string>
using namespace std;

class person
{
public:
	int age;
  //按参数分为:有参构造和无参构造(默认构造)
	person() {
		cout << "person的无参构造调用" << endl;
	}
	person(int a) {
		age = a;
		cout << "person的有参构造调用" << endl;
	}
	//按类型分为:拷贝构造和普通构造(有参构造和无参构造)、
	person(const person &p) //相当于传入一个person到参数按照这个person拷贝一份
	{
		age = p.age;
		cout << "person的拷贝构造调用" << endl;
	}
};
void test01()
{    //括号法调用
	person p1;/*无参构造调用 调用无参构造时不要加()
	否则编译器会认为这是一个函数的声明不是在创建对象*/
	person p2(10); //有参构造调用
	person p3(p2); //拷贝构造调用

	cout << "p2的年龄:" << p2.age << endl;
	cout << "p3的年龄" << p3.age << endl; //对p2进行拷贝
}
int main() {
	test01();
	return 0;
}

{5CA9486F-4E61-485D-A52F-25E85BF4872E}.png

2.2.1显示法和隐式转化法调用

#include<iostream>
#include<string>
using namespace std;

class person
{
public:
	int age;
  //按参数分为:有参构造和无参构造(默认构造)
	person() {
		cout << "person的无参构造调用" << endl;
	}
	person(int a) {
		age = a;
		cout << "person的有参构造调用" << endl;
	}
	//按类型分为:拷贝构造和普通构造(有参构造和无参构造)、
	person(const person &p) //相当于传入一个person到参数按照这个person拷贝一份
	{
		age = p.age;
		cout << "person的拷贝构造调用" << endl;
	}
};
void test01()
{    //显示法调用
	person p1;//无参构造调用
	person p2= person(10); //有参构造调用
	person p3=person (p2); //拷贝构造调用
	cout << "p2的年龄:" << p2.age << endl;
	cout << "p3的年龄" << p3.age << endl;

	//隐式转化法
	person p4 = 10;  //相当于写了person p4= person(10)
	person p5 = p4;
}
int main() {
	test01();
	return 0;
}

{27E1A5F6-50E3-4068-9F6A-C93960A85254}.png

2.3拷贝构造函数调用时机

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

#include<iostream>
#include<string>
using namespace std;
class person
{
public:
	int age;
	person() {
		cout << "person的无参构造函数调用" << endl;
	}
	person(int a) {
		age = a;
		cout << "person的有参构造函数调用" << endl;
	}
	person(person &p) {
		age = p.age;
		cout << "person的拷贝构造函数调用" << endl;
	}

	~person() {
		cout << "person的无参析构函数调用" << endl;
	}
};
//1.使用一个已经创建完毕的的对象来初始化一个新对象
void test01() {
	person p1(20);
	person p2(p1);
	cout << "p2的年龄为:" << p2.age << endl;
}


int main() {
	test01();
	

	system("pause");
	return 0;
}

{C022BCB2-E6D8-4A4E-8D98-C933655A1A88}.png

2..值传递的方式给函数参数传值 #include #include using namespace std; class person { public: int age; person() { cout << "person的无参构造函数调用" << endl; } person(int a) { age = a; cout << "person的有参构造函数调用" << endl; } person(person &p) { age = p.age; cout << "person的拷贝构造函数调用" << endl; }

~person() {
	cout << "person的无参析构函数调用" << endl;
}

}; //2.值传递的方式给函数参数传值 void dowork(person p) {

} void test02() { person p; dowork(p); }

int main() { test02();

system("pause");
return 0;

}

{CD409575-BFC4-472E-8B39-E4DADD2F9D9C}.png

    #include<iostream>
#include<string>
using namespace std;
class person
{
public:
	int age;
	person() {
		cout << "person的无参构造函数调用" << endl;
	}
	person(int a) {
		age = a;
		cout << "person的有参构造函数调用" << endl;
	}
	person(person &p) {
		age = p.age;
		cout << "person的拷贝构造函数调用" << endl;
	}

	~person() {
		cout << "person的无参析构函数调用" << endl;
	}
};
//3.值方式返回局部对象
person dowork2() {
	person p1;
	return p1;
}
void test03() {
	person p = dowork2();//这里发生错误不懂问完再改
}


int main() {
	test03();
	

	system("pause");
	return 0;
}

2.4构造函数调用规则

默认情况下,编译器至少给一类三 个函数 1.默认构造函数(无参,函数体为空) 2.默认析构函数(无参,函数体为空) 3.默认拷贝构造函数,对属性进行值拷贝

构造函数调用规则如下: 如果用户定义有参构造函数,则编译器不再提供默认无参构造,但会提供默认拷贝构造 如果用户定义拷贝构造函数,则编译器不再提供其他构造函数

2.5深拷贝和浅拷贝

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

浅拷贝:

    #include<iostream>
#include<string>
using namespace std;
class Person
{
public:
	int m_age;
	int *m_height;
	Person() {

		cout << "Presond的默认构造函数调用" << endl;
	}
	Person(int age,int height) {
		m_age = age;
		m_height = new int(height);//用指针去接受堆区的数据
		cout << "Presond的有参构造函数调用" << endl;
	}
	~Person() {
		//析构代码将堆区开辟数据做释放操作
		if (m_height != NULL /*(空的意思)*/) {
			delete m_height;
			m_height = NULL;//释放后防止野指针的出现将其置空
		}
		cout << "Presond的析构函数调用" << endl;
	}
};
void test01() {
	Person p1(18,180);
	cout << "p1 age:" << p1.m_age<<"p1 height"<<p1.m_height << endl;
	Person p2(p1);
	cout << "p2 age:" << p2.m_age << "p2 height" << p2.m_height << endl;//我们没有提供拷贝构造函数,编译器做了浅拷贝的操作给我们提供了一个拷贝构造函数
}



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

运行截图:

屏幕截图 2024-10-29 171258.png

浅拷贝问题:堆区内存重复释放

{4F111D03-5FF6-4A1B-9BED-9B8064462CB6}.png

解决办法利用深拷贝开辟一块新的内存区域

{90455041-1A4B-4557-BC31-D23AEDAD48C6}.png

#include<iostream>
#include<string>
using namespace std;
class Person
{
public:
	int m_age;
	int* m_height;
	Person() {
		m_height = nullptr;
		cout << "Presond的默认构造函数调用" << endl;
	}
	Person(int age, int height) {
		m_age = age;
		m_height = new int(height);//用指针去接受堆区的数据
		cout << "Presond的有参构造函数调用" << endl;
	}
	Person(const Person& p) {
		m_age = p.m_age;
		//m_height = p.m_height;编译器默认实现的就是这行代码
		//进行深拷贝操作
		m_height = new int(*p.m_height);

	}
	~Person() {
		//析构代码将堆区开辟数据做释放操作
		if (m_height != nullptr /*(空的意思)*/) {
			delete m_height;
			m_height = nullptr;//释放后防止野指针的出现将其置空
		}
		cout << "Presond的析构函数调用" << endl;
	}

};
void test01() {
	Person p1(18, 180);

	cout << "p1 age:" << p1.m_age << "p1 height:" << *p1.m_height << endl;
	Person p2(p1);
	cout << "p2 age:" << p2.m_age << "p2 height:" << *p2.m_height << endl;
}


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

{8DDB0A26-D225-4F1A-BB03-616F6154578D}.png

2.6初始化列表

作用:c++提供初始化列表语法,用于初始化属性

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

    #include<iostream>
#include<string>
using namespace std;

	class preson {
	public:
		preson(int a, int b, int c) :m_A(a), m_B(b),m_C(c){}

		void printperson() {
			cout << "a=" << m_A << endl;
			cout << "b=" << m_B << endl;
			cout << "c=" << m_C << endl;
		}
	private:
		int m_A;
		int m_B;
		int m_C;


	};
	
		


int main() {
	preson p (20,30,40);
	p.printperson();
	

	system("pause");
	return 0;
}

{2FB6B96B-01D9-4766-8CEC-248818C07C92}.png

2.7类对象作为类成员

    #include<string>
using namespace std;
      //类对象作为类成员
class phone {
public:
	phone(string PName)
	{
		m_PName = PName;

	}
	string m_PName;  //手机品牌名称
	 }; 
class person {
public:
	//phone m_PName = PName;(隐式转换法) 编译器自动利用PName给m_PName进行构造调用
	person(string Name, string  PName) :m_Name(Name), m_PName(PName) {


	}

	string  m_Name;
	phone  m_PName;

};
void test() {
	person p("张三", "华为");

	cout << p.m_Name << "拿着" << p.m_PName.m_PName<<"手机" << endl;

}



	
		


int main() {
	
	test();

	system("pause");
	return 0;
}

{6A8EFA2D-12AB-4D2C-B7F5-2CE18EBE8810}.png

3 静态成员

静态成员就是在成员变量或者成员函数前加关键字static

静态成员分为:

3.1静态成员变量:

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

2.在编译阶段分配年存

3.类内声明类外初始化


    #include<iostream>
#include<string>
using namespace std;
      //类对象作为类成员
class person {
public:
	static int m_A; //类内声明静态成员变量

};
int person::m_A = 10; //类外初始化
void test() {
	//静态成员变量的两种访问方式
	//1.通过对象
	person p1;
	p1.m_A = 100;
	cout << "a=" << p1.m_A << endl;

	person p2;
	p2.m_A = 200;
	cout << "2a=" << p2.m_A << endl;
	cout << "a=" << p1.m_A << endl;//共享同一份数据

	//static int m_A 只有一份内存空间,被所有 person 类的对象共享
	//当通过 p1 修改 m_A 为 100 时,所有对象看到的 m_A 都是 100
	//当通过 p2 修改 m_A 为 200 时,所有对象看到的 m_A 都变成了 200

	//2.通过类名
	cout << "m_A" << person::m_A << endl;
}


int main() {
	
	test();

	system("pause");
	return 0;
}

{F672BCBE-F703-4620-8273-4FF0BA467BCE}.png

3.2静态成员函数

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

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

*类外访问不到私有静态成员变量

    #include<iostream>
#include<string>
using namespace std;

class Person {
public:
    static void  func()//静态成员函数调用
    {
        m_A = 100;//静态成员函数可以访问静态成员变量,不可以访问非静态成员变量
        cout << "static void  func的调用" << endl;
  }
    static int m_A;//静态成员变量
};
int Person::m_A = 0;//静态成员类内声明类外初始化

void test01() {
   //1.通过对象访问
    Person p;
    p.func();
    //2.通过类名访问
    Person::func();
}

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

屏幕截图 2024-10-31 154309.png

4.c++对象模型和this指针

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

    #include<iostream>
#include<string>
using namespace std;
//成员变量和成员函数分开储存
class Person {
public:
    int m_A;//非静态成员变量,属于类对象上
    static int m_B;//静态成员变量,不属于类对象上
    void func() {}; //非静态成员函数,不属于类对象上
    static void func1() {};//静态成员函数,不属于类对象上
};


void test01() {
    Person p;
    cout << "size of p=" << sizeof(p) << endl; //空对象占用内存空间为1,因为编译器会给每个空对象分配一个字节空间,
    //是为了区分空对象占内存的位置,每个空对象也应该有独一无二的内存地址

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

4.2 this指针概念