C++研发 【面向对象编程阶段总结】

本文详细介绍了C++编程中的封装概念,对象初始化和清理(包括构造函数、析构函数及静态成员)、this指针的使用、对象模型、运算符重载和继承机制,以及友元和常用操作符的重写技巧。

一、目录

1、封装

2、对象初始化和清理

3、对象模型与this指针

4、友元

5、运算符重载

6、继承

二、封装

与C语言的面向过程思想不同,

在C++编程中,我们将万事万物看成对象,并把具有相同性质的对象抽象成类。

我们将属性和行为作为一个整体,用这些整体来表现生活中的事物。同时,我们可以将属性和行为加以权限控制。

重点:

1、访问权限:

1、公共权限  public        类内可以访问  类外可以访问
2、保护权限  protected  类内可以访问  类外不可以访问 儿子也可以访问父亲中的保护内容
3、私有权限  private       类内可以访问  类外不可以访问 儿子不可以访问父亲中的私有内容

 举例:人类

class Person
{
	//姓名  公共权限
public:
	string m_Name;
 
	//汽车  保护权限
protected:
	string m_Car;
 
	//银行卡密码  私有权限
private:
	int m_Password;
 
public:
	void func()
	{
		m_Name = "张三";
		m_Car = "拖拉机";
		m_Password = 123456;
	}
};

三、对象初始化和清理

重点:

1、对象初始化和清理涉及安全问题。

2、通过构造函数和析构函数解决上述问题。

3、构造函数:

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

构造函数语法:类名(){}

  1. 构造函数,没有返回值也不写void
  2. 函数名称与类名相同
  3. 构造函数可以有参数,因此可以发生重载
  4. 程序在调用对象时候会自动调用构造,无须手动调用,而且只会调用一次

4、析构函数

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

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

  1. 析构函数,没有返回值也不写void
  2. 函数名称与类名相同,在名称前加上符号 ~
  3. 析构函数不可以有参数,因此不可以发生重载
  4. 程序在对象销毁前会自动调用析构,无须手动调用,而且只会调用一次

5、构造函数共有三种:

无参构造,有参构造,拷贝构造。

默认情况,C++编译器至少给一个类添加三个函数:

无参构造,拷贝构造,析构函数。

如果我们自主实现了有参构造,C++不再提供无参构造;

如果我们自主实现了拷贝构造,C++不再提供任何构造。

class Person
{
public:
	int m_age;

public:
	Person()
	{

	}

	Person(int age)
	{
		m_age = age;
	}

	Person(const Person& p)
	{
		m_age = p.m_age;
	}
};

【注】 拷贝构造传参传进的是引用,并且我们为了保证被拷贝的对象的数据安全,这里使用const关键字。 

6、静态成员

  • 静态成员变量
    • 所有对象共享同一份数据
    • 在编译阶段分配内存
    • 类内声明,类外初始化
  • 静态成员函数
    • 所有对象共享同一个函数
    • 静态成员函数只能访问静态成员变量
class Person
{
public:
	int m_age;
	static int tall;

public:
	Person()
	{

	}

	Person(int age)
	{
		m_age = age;
	}

	Person(const Person& p)
	{
		m_age = p.m_age;
	}

	static void func()
	{
		cout << "static func!" << endl;
	}
};
int Person::tall = 10;

int main()
{
	Person p1;
	p1.m_age = 1;
	Person p2;
	p2 = p1;
	cout << p2.m_age<<endl;
	cout << p1.tall;
	p1.tall = 20;
	cout << p2.tall;
	Person::func();
	p1.func();
	return 0;
}

四、对象模型与this指针

重点:

1、C++对象模型中,空对象占用1个字节的内存空间。

2、只有非静态成员变量属于对象空间。

3、this指针是一个指针常量,它指向被调用的成员函数所属对象。

4、this指针可以用来区分同名的形参与成员变量。

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


Person PersonAddPerson(Person p)
	{
		this->age += p.age;
		//返回对象本身
		return *this;
	}

 比如这里,我们执行下面代码:

	Person p1;
	p1.m_age = 1;
	Person p2;
	p2 = p1;
	p1.PersonAddPerson(p2);
	cout << p1.m_age;

得到的结果是2。

虽然这里是传值函数,

但是由于this始终指向的是调用这个函数的对象,也就是p1,

所以只要是对this->的成员变量进行修改,

都会作用在原对象上。 

6、const修饰成员函数和对象后称之为常函数和常对象。常函数内不可修改成员属性,但如果在成员属性声明时加关键字mutable后,在常函数中依然可以修改。


class Person {
public:
	Person() {
		m_A = 0;
		m_B = 0;
	}
 
	//this指针的本质是一个指针常量,指针的指向不可修改
	//如果想让指针指向的值也不可以修改,需要声明常函数
	void ShowPerson() const {
		//const Type* const pointer;
		//this = NULL; //不能修改指针的指向 Person* const this;
		//this->mA = 100; //但是this指针指向的对象的数据是可以修改的
 
		//const修饰成员函数,表示指针指向的内存空间的数据不能修改,除了mutable修饰的变量
		this->m_B = 100;
	}
        //在成员函数后面加const,修饰的是this指向,相当于:const Person* const this;
        //让指针指向的值也不可以修改。
 
	void MyFunc() const {
		//mA = 10000;
	}
 
public:
	int m_A;
	mutable int m_B; //可修改 可变的
};
 
 
//const修饰对象  常对象
void test01() {
 
	const Person person; //常量对象  
	cout << person.m_A << endl;
	//person.mA = 100; //常对象不能修改成员变量的值,但是可以访问
	person.m_B = 100; //但是常对象可以修改mutable修饰成员变量
 
	//常对象访问成员函数
	person.MyFunc(); //常对象只能调用const的函数
 
}
 
int main() {
 
	test01();
 
	system("pause");
 
	return 0;
}

五、友元

1、全局函数和类均可做友元,

声明方式为:

在类的大括号下第一行写上全局函数和类,

并在最前方加上friend关键字。

如:

全局函数:

friend void goodGay(Building * building);

 类:

friend class goodGay;

别的类中的成员函数:

friend void goodGay::visit();

成为某个类中的友元后,即可访问其私有属性的成员或函数。

 

六、运算符重载

1、使用operator关键字进行重载。

2、<<运算符是ostream类的一个对象,且只能有一个。

重写输出函数举例:


ostream& operator<<(ostream& out, Person& p) {
	out << "a:" << p.m_A << " b:" << p.m_B;
	return out;
}

3、写自增运算符时,需要考虑两种情况并都实现,

如果只实现一种,会报错。

第二种由于不满足重载条件,可以使用占位参数。

	Person& operator++()
	{
		this->m_age++;
		return *this;
	}

	Person& operator++(int)
	{
		Person temp = *this;
		this->m_age++;
		return temp;
	}

七、继承

1、继承方式一共有三种:

  • 公共继承
  • 保护继承
  • 私有继承

2、从父类继承过来的私有成员虽然访问不到,但只是被隐藏了,还是会继承下去。

可以利用工具查看:

打开工具窗口后,定位到当前CPP文件的盘符

然后输入: cl /d1 reportSingleClassLayout查看的类名 所属文件名

3、继承中,先调用父类构造函数,再调用子类构造函数,析构顺序与构造相反

4、访问父类同名成员 需要加作用域

class Base {
public:
	Base()
	{
		m_A = 100;
	}
 
	void func()
	{
		cout << "Base - func()调用" << endl;
	}
 
	void func(int a)
	{
		cout << "Base - func(int a)调用" << endl;
	}
 
public:
	int m_A;
};
 
 
class Son : public Base {
public:
	Son()
	{
		m_A = 200;
	}
 
	//当子类与父类拥有同名的成员函数,子类会隐藏父类中所有版本的同名成员函数
	//如果想访问父类中被隐藏的同名成员函数,需要加父类的作用域
	void func()
	{
		cout << "Son - func()调用" << endl;
	}
public:
	int m_A;
};
 
void test01()
{
	Son s;
 
	cout << "Son下的m_A = " << s.m_A << endl;
	cout << "Base下的m_A = " << s.Base::m_A << endl;
 
	s.func();
	s.Base::func();
	s.Base::func(10);
 
}
int main() {
 
	test01();
 
	system("pause");
	return EXIT_SUCCESS;
}

5、利用虚继承解决菱形继承问题

 


class Animal
{
public:
	int m_Age;
};
 
//继承前加virtual关键字后,变为虚继承
//此时公共的父类Animal称为虚基类
class Sheep : virtual public Animal {};
class Tuo   : virtual public Animal {};
class SheepTuo : public Sheep, public Tuo {};
 
void test01()
{
	SheepTuo st;
	st.Sheep::m_Age = 100;
	st.Tuo::m_Age = 200;
 
	cout << "st.Sheep::m_Age = " << st.Sheep::m_Age << endl;
	cout << "st.Tuo::m_Age = " <<  st.Tuo::m_Age << endl;
	cout << "st.m_Age = " << st.m_Age << endl;
}
 
 
int main() {
 
	test01();
 
	system("pause");
 
	return 0;
}

这里运行结果:

 

 

 

 

 

 

 

 

 

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值