110-C++面向对象

本文详细介绍了C++的面向对象特性,包括构造与析构、拷贝构造与深拷贝、赋值运算符重载、访问权限、struct与class的区别、封装、继承、多态、静态成员、初始化列表、引用、常量、作用域、常对象与常方法、单例模式,以及内存管理和类型限定符的使用规范。
摘要由CSDN通过智能技术生成

一:知识点

1.先构造的后析构,后构造的先析构

2.拷贝构造,同样也是一个构造函数,Person(Person & src){};与构造函数是重载的关系

下面两种方式都是拷贝构造

Person p2(p1);
Person p2 = p1;

拷贝构造会在使用同类型的对象构造新对象的时候自动调用

如果没有自己实现拷贝构造,编译器会自动生成一个浅拷贝的拷贝构造函数

拷贝构造要防止浅拷贝
原因:浅拷贝会导致两个指针指向同一块堆上的空间,当析构的时候,第二次析构会导致程序崩溃(重复释放一块堆上的空间会导致崩溃),所以要深拷贝

拷贝构造要防止自赋值:给野指针复制是错误的

(面试常问)拷贝构造必须传引用,否则会导致死递归,原因是重复使用同类型的对象构造新对象

Person(Person src)
{}

Person p1("zhangsan",23,1);
Person p2(p1);//用p1拷贝构造p2

当用p1拷贝p2的时候,需要将实参传给实参Person src(p1),又是拷贝构造,然后再把实参传给形参Person src(p1),这样就会导致死递归

3.等号运算符重载

使用同类型的对象给同类型的对象赋值的时候,会自动调用等号运算符重载

如果没有自己实现等号运算符重载,编译器会自动生成一个浅拷贝的等号运算符重载

4.类中的权限

public(公共的):在任何地方都能访问,在类内类外都可以访问

private(私有的):只能当前类中进行访问

放在public里面还是private里面并没有明确的规定,但是成员变量一般都放在private,函数放在public里面,如果某些函数不希望外界调用,就可以放在private里面

5.strcut和class的区别

在C++中,struct和class都是类,只不过struct里面的默认属性是public,class里面的默认属性是private

6.内置类型可以用等号连接,自定义类型正常来说不可以用等号连接,没有连接的规则

7.有的说面向对象是三大特征:①封装②继承③多态

有的说面向对象是四大特征:①封装②继承③多态④抽象

8.常量必须初始化,编译期的时候会把用到常量的地方会自动替换为常量的值,如果不初始化,后面就不能再赋值了

9.初始化列表:是在构造函数即将执行的时候执行的操作

10.引用也必须初始化,引用的底层是一个指针,所有用到引用的地方都会替换为指针的解引用,初始化之后就无法改变引用的指向了,也就不能再赋值了

11.如果类中存在必须初始化的成员变量(如常量、引用等),就必须手动写到初始化列表

12.static定义的变量存储在数据段

13.静态成员,一个类只有一份,整个类所共用的,其他成员变量,每个对象都有一份

静态成员,必须在类外进行初始化

静态成员的访问不依赖于this指针,所以不依赖于对象,可以通过作用域访问静态函数

静态的成员方法里面只能用静态成员,因为没有this指针,所有的成员变量都使用不了

14.::表示作用域,通过作用域访问没有this指针

通过作用域可以访问静态成员变量和静态成员方法

15.(面试重点)注意:不能把常量的地址泄露给非常量的指针,这是一个非常严重的问题

16.常对象只能调用常方法,原因:不能把常量的地址泄露给非常量的指针

常对象调用静态方法是可以的

静态方法一般不适用对象调用,一般使用作用域调用

17.什么时候需要把方法写成const?

方法内不会修改成员变量的就写成const

不一定修改的就提供两个,让他们重载

18.单例模式:

19.delete NULL;和free(NULL);都是对的,不要delete、free野指针

20.构造函数(拷贝构造也算是构造函数)和析构函数不允许使用类型限定符(如const)

二:代码

//面向对象
#include <iostream>
using namespace std;//命名空间

int school = 985;
int Person::_num = 0;//静态变量必须在类外进行初始化

class Person
{
private:
	char* _name;
	int _age;
	const int _sex;//性别不会改变,所以加const
	int& _school;//引用必须初始化

public:

	static int _num;//静态成员,一个类只有一份,整个类所共用的,静态成员,必须在类外进行初始化

	Person()//默认构造函数,没有参数,当然肯定有一个this指针,一般不说这个参数
		:_sex(1),_school(school)//初始化列表,在调用构造函数之前执行
	{
		cout << "Person()" << endl;
		_name = NULL;
		_num++;
	}

	//构造函数
	Person(const char* name, int age, int sex)
		:_sex(sex), _school(school)//初始化列表
	{
		cout << "Person(const char *name,int age,int sex)" << endl;
		_name = new char[strlen(name) + 1];
		for (int i = 0; i < strlen(name) + 1; i++)
		{
			_name[i] = name[i];
		}
		_age = age;
		_num++;
	}

	//拷贝构造在使用同类型的对象构造新对象的时候自动调用的
	//如果没有自己实现拷贝构造,编译器会自动生成一个浅拷贝的拷贝构造函数
	//浅拷贝:防止浅拷贝
	//(***面试常问)拷贝构造必须传引用,否则会导致死递归,为什么?
	// 原因:Person p2(p1),如果不是引用的话,就是Person (Person src)会将实参传给形参Person src(p1),
	// 这又是一个拷贝构造,然后又把实参传给形参Person src(p1),就会导致死递归
	//src传进来不会被改动,所以加一个const,也为了防止传进来的对象是一个常对象,如果不加const,就会导致把
	//常量的地址泄露给非常量的指针,这是一个错误,所以要用常引用
	Person(const Person& src)
		:_sex(src._sex), _school(school)
	{
		cout << "Person(Person & src)" << endl;
		_name = new char[strlen(src._name) + 1];//深拷贝
		for (int i = 0; i < strlen(src._name) + 1; i++)
		{
			_name[i] = src._name[i];
		}
		_age = src._age;
		_num++;
		//_name = src._name;浅拷贝,两个类的_name都指向同一个堆上的空间,当第二次析构的时候会导致程序崩溃
		//_age = src._age;
		//_sex = src._sex;
	}

	//等号运算符重载
	//是在使用同类型的对象给同类型的对象赋值的时候自动调用的
	//如果没有自己实现,编译器会自动生成一个浅拷贝的等号运算符重载
	//加一个const,也为了防止传进来的对象是一个常对象,如果不加const,就会导致把
	//常量的地址泄露给非常量的指针,这是一个错误。所以要用常引用
	Person& operator = (const Person& src)
	{
		cout << "void operator = (Person& src)" << endl;
		//_name = src._name;//这是浅拷贝,会崩溃

		//防止自己给自己赋值
		//如果不加这条语句,在经过下面的delete[] _name之后,_name就变成了野指针
		if (this == &src)//如果当前对象的指针和传进来的指针相同,说明指向的是同一个对象
		{
			return *this;
		}

		//防止内存泄露,之前的_name指向其他的地方了,经过下面的拷贝后,之前指向的地方就丢失了,
		//所以要先释放之前的内存,防止内存泄露
		delete[]_name;
		
		_name = new char[strlen(src._name) + 1];
		for (int i = 0; i < strlen(src._name) + 1; i++)
		{
			_name[i] = src._name[i];
		}
		_age = src._age;
		
		return *this;
	}

	//提供公有的方法,可以让外界获取到信息
	const char* get_name(/* const Person * this */)const
	{
		return _name;
	}

	char* get_name(/* Person * this */)
	{
		return _name;
	}

	int get_age(int is_police)
	{
		if (is_police)
		{
			return _age;
		}
		return 18;
	}

	void add_age()
	{
		_age++;
	}

	void Show()
	{
		cout << _name << "  " << _age << "  " << _sex << endl;
	}
	
	//析构函数
	~Person()
	{
		cout << "~Person" << endl;
		delete[]_name;
		_num--;
	}
};



int main()
{
	//const Person p1 ("laoliu",23,1);
	//p1.Show();
	
	//Person p2(p1);//用p1来构造p2
	//p2.Show();

	//Person p2(p1);//拷贝构造
	//Person p3 = p1;//也是拷贝构造

	//p3 = p1;//cpp文件可以,c文件不可以
	//内置类型可以用等号连接,自定义类型正常来说不可以用等号连接,没有连接的规则
	//p3 = p3;
	//p3.show();

	//cout << Person::_num << endl;;

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值