第二周学习报告

补充上周的一个深拷贝案例操作// m_Height=new int (*p.m_Height);

接着上周的顺序继续写

7)初始化列表

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

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

                        //Person(int a,int b): m_A = (a), m_B = (b){}

8)类对象作为成员

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

        下图中Phone为Person中的对象成员

#include<iostream>
#include<string>
using namespace std;
class Phone
{
public:
	string PName; 
	Phone(string name)
	{
		PName=name;
	}
};
class Person
{
public:
	string Name;
	Phone m_Phone;
	//Phone m_Phone=Pname 隐式转换法 
	Person(string name,string Pname):Name(name),m_Phone(Pname)
	{
		
	}
};
void test()
{
	Person p("张三","遥遥领先");
	cout<<p.Name<<"拿着"<<p.m_Phone.PName<<endl;
}
int main()
{
	test();
	return 0;
}

 顺序:当其他类对象作为本类成员,构造时候先构造类对象,在构造自身

            析构时先释放构造自身,在释放构造类对象

9)静态成员(在成员函数和成员变量前加上关键字static)

静态成员也有访问权限

(1)静态成员变量

        所有对象共享同一份数据

        在编译阶段分配内存

        类内声明,类外初始化

                在类内中有static int 变量

                在类外中有int 类名::变量(=值)

        可以通过对象访问   (对象.变量)

        可以通过类名访问(类名::变量)

(2)静态成员函数   static 类型 函数名(){}  

        所有对象共享同一个函数

        静态成员函数只能访问静态成员变量(不能访问非静态成员变量,无法区分到底是哪个对象的)       

通过对象访问(对象.函数名)

通过类名访问(类名::函数名)

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

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

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

        空对象占用内存空间为:1

2)this指针概念(this指针指向被调用的成员函数所属的对象)

this指针是隐含每一个非静态成员函数内的一种指针(本质是:类名*const this)常量指针,指向不可修改

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

用途:

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

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

在进行重复的函数操作时可以返回原函数值,如add为执行加法的构造函数操作

                遇到p.add(10).add(10).add(10).add(10)时在add中return *this;后就能进行此操作

3)空指针访问成员函数

        c++中空指针也可以调用成员函数的,但是也要注意有没有用到this指针

        如果用到this指针,需要加以判断保证代码的健壮性

#include<iostream>
using namespace std;
class Person
{
public:
	int m_Age;
	void showname()
	{
		cout<<"showname"<<endl;
	}
	void showage()
	{
		if(this==NULL) return;//防止空指针输入崩溃 
		cout<<m_Age<<endl;
	}
};
void test()
{
	Person *p;
	p->showage();
	p->showname(); 
}
int main()
{
	test();
	return 0;
} 

4)const修饰成员函数

常函数:

         成员函数后加const后我们称为这个函数为常函数

        常函数内不可修改成员属性

        成员属性声明时加关键字mutable后,在常函数中依然可以修改//如mutable int b;

        用法 类型 函数名()const{}//加入const后内部的值不可修改

常对象:

        声明常对象前加const称该对象为常对象(const 类名 变量;)

        常对象只能调用常函数(因为不同成员函数的属性可以修改)

        加了mutable的变量后在常函数中也可以修改

7.友元(friend)(在下面8.2的左移运算符重载中有示例)

目的:让一个函数或者类访问另一个类中的私有成员

1)全局函数做友元

将声明的全局函数放到类中并在前面放一个friend,就可以使得全局函数访问到类中的私有成员(只需写到类中即可)

        friend 类型 函数名();              最后加一个“;”

2)类做友元

        在类外中也可以写成员函数,需要加一个作用域          类名::函数名(){} 

        用法与(1)中大体相同      friend class 类名 

3)成员函数做友元

        friend 类型 类名::成员函数名();

8.运算符重载

        概念:对已有的运算符进行重新定义,赋予其另一种功能,以适应不同的数据类型

1)加号运算符重载

        对于内置的数据类型的表达式的运算符是不可能改变的

        不要滥用运算符重载

        作用:实现两个自定义数据类型的相加运算

成员函数重载+号//类名 operator+(变量){操作}

#include<iostream>
using namespace std;
class Person
{
public:
	int A;
	int B;
	Person operator+(Person &p)
	{
		Person t;
		t.A=this->A+p.A;
		t.B=this->B+p.B;
		return t;
	}
};
void test()
{
	Person p1;
	p1.A=10;
	p1.B=10;
	Person p2;
	p2.A=10;
	p2.B=20;
	Person p3=p1+p2;
	cout<<p3.A<<endl;
	cout<<p3.B<<endl;
}
int main()
{
	test();
	system("pause");
	return 0;	
} 

也可以全局函数重载+号

#include<iostream>
using namespace std;
class Person
{
public:
	int A;
	int B;
	
};
Person operator+(Person &p1,Person &p2)
	{
		Person t;
		t.A=p1.A+p2.A;
		t.B=p1.B+p2.B;
		return t;
	}
void test()
{
	Person p1;
	p1.A=10;
	p1.B=10;
	Person p2;
	p2.A=10;
	p2.B=20;
	Person p3=p1+p2;
	cout<<p3.A<<endl;
	cout<<p3.B<<endl;
}
int main()
{
	test();
	system("pause");
	return 0;	
} 

还可以重载不同类型的变量如p1与10相加

2)左移运算符重载

        可以输出自定义数据类型

        只能利用全局函数重载左移运算符

        重载左移运算符配合友元可以实现输出自定义数据类型

#include<iostream>
using namespace std;
class Person
{
	friend ostream &operator<<(ostream &out,Person &p);//友元 
public:
	Person(int a,int b)
	{
		m_A=a;
		m_B=b;
	}
private:
	int m_A;
	int m_B;
};
ostream &operator<<(ostream &out,Person &p)//全局函数重载左移符号 
{
	out<<"m_A="<<p.m_A<<" m_B="<<p.m_B;
	return out;
}
void test()
{
	Person p(10,10);
	cout<<p<<"hello"<<endl;
}
int main()
{
	test();
	system("pause");
	return 0;
}

3)递增运算符重载

        作用:通过重载递增运算符,实现自己的整型数据

#include<iostream>
using namespace std;
class MyInteger
{
	friend ostream &operator<<(ostream &out,MyInteger p);//友元 
public:
	MyInteger()
	{
		m_A=0;
	}
	//前置重载++运算符,需要用引用 
	MyInteger &operator++()
	{
		m_A++;
		return *this;
	}
	//重载后置++运算符,不需要引用 (int)代表占位参数,可以区分前置和后置递增 
	MyInteger operator++(int)
	{
		MyInteger t=*this;
		m_A++;
		return t;
	} 
private:
	int m_A;
};
ostream &operator<<(ostream &cout,MyInteger p)//全局函数重载左移符号 
{
	cout<<"m_A="<<p.m_A;
	return cout;
}
void test()
{
	MyInteger p;
	cout<<"前置为"<<++p<<endl;
	cout<<"后置为"<<p++<<endl; 
	cout<<p<<endl;
}
int main()
{
	test();
	system("pause");
	return 0;
}

这里在左移重载时注意类的参数不需要引用。

前置递增返回引用,后置递增返回值

4)赋值运算符重载

        c++编译器至少给一个类添加4个函数

        默认构造函数

        默认析构函数

        默认拷贝函数

        赋值运算符 operator=,对属性进行拷贝

如果类中有属性指向堆区,做赋值操作时也会出现深浅拷贝问题

示例

#include<iostream>
using namespace std;
class Person
{
public:
	Person(int age)
	{
		m_Age=new int(age);//堆区建立 
	}
	//重载赋值运算符 
	Person &operator=(Person &p)
	{
		if(m_Age!=NULL)
		{
			delete m_Age;
			m_Age=NULL;
		}
		//提供深拷贝,解决浅拷贝问题
		m_Age=new int(*p.m_Age);
		return *this; 
	}
	~Person()
	{
		if(m_Age!=NULL)
		{
			delete m_Age;
			m_Age=NULL;
		}
	}
	int *m_Age;
};
void test()
{
	Person p1(18);
	Person p2(20);
	Person p3(22);
	p3=p2=p1;
	cout<<"p1的年龄为"<<*p1.m_Age<<endl;
	cout<<"p2的年龄为"<<*p2.m_Age<<endl;
	cout<<"p3的年龄为"<<*p3.m_Age<<endl;
}
int main()
{
	test();
	system("pause");
	return 0; 
}

5)关系运算符重载

        作用:可以让两个自定义类型对象进行对比操作

#include<iostream>
#include<string>
using namespace std;
class Person
{
public:
	string m_name;
	int m_age;
	Person(string name,int age)
	{
		this->m_age=age;
		this->m_name=name;
	};
	bool operator==(Person &p)
	{
		if(this->m_name==p.m_name&&this->m_age==p.m_age) return true;
		else return false;
	}
	bool operator!=(Person &p)
	{
		if(this->m_name!=p.m_name&&this->m_age!=p.m_age) return true;
			else return false;
	}
};
void test()
{
	Person p1("张三",18); 
	Person p2("张三",18); 
	if(p1==p2) cout<<"p1和p2相等"<<endl;
		else cout<<"p1和p2不相等"<<endl;
	if(p1!=p2) cout<<"p1和p2不相等"<<endl;
		else cout<<"p1和p2相等"<<endl;
} 
int main()
{
	test();
	system("pause");
	return 0;
}

6)函数调用运算符重载

        函数调用运算符()也可以重载

        由于重载后使用的方式非常像函数的调用,因此称为仿函数

        仿函数没有固定写法,非常灵活

示例打印函数

#include<iostream>
#include<string>
using namespace std;
class Print
{
public:
	void operator()(string test)
	{
		cout<<test<<endl;
	}
};
void test()
{
	Print p;	
	p("Hello");
}
int main()
{
	test();
	system("pause");
	return 0;
}

此重载可以用来做各种各样的操作,非常灵活

9.继承(可以把公共拥有的部分写在一起,不用重复写代码,只需写一遍重复调用即可)

1)继承的基本语法

        好处:减少重复代码

        子类也称派生类 父类也称基类

        派生类中的成员,包含两大部分:

        一类是从基类继承过来的,一类是自己增加的成员。

        从基类继承过来的表现其共性,而新增的成员体现了其个性。

2)继承方式

        语法:class 子类:继承方式 父类

        三种继承方式:公共继承,保护继承,私有继承

        具体继承方式

3)继承中的对象模型

        父类中的所有非静态成员属性都会被子类继承下去

        父类中的私有成员属性 是被编译器给隐藏了,因此是访问不到,但是确实被继承下去了

        利用开发人员命令提示工具查看对象模型(英文名为Developer Command Prompt for VS 版本

        跳转至程序所保存的盘符

        跳转文件路径cd具体路径下(复制粘贴路径)

        查看命名

        输入cl/d1 reportSingleClassLayot类名 文件名 

        效果图

4)继承中构造和析构顺序

        子类继承父类后,当创建子类对象,也会调用父类的构造函数

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

5)继承同名成员处理方式

        子类对象可以直接访问到子类中同名成员

        子类对象加作用域可以访问到父类同名成员

        当子类与父类拥有同名的成员函数,子类会隐藏父类中同名成员函数,加作用域可以访问到父类中同名函数

6)继承中同名静态成员处理方式

        静态成员和非静态成员出现同名,处理方式一致

        访问子类同名成员 直接访问即可

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

        访问方式两种使用方式看 7.9)的静态成员

        在通过类名访问过程中有//类名::作用域类名::成员变量

        与 5)中的特点基本相同

7)多继承语法

        c++中允许一个类继承多个类

        语法:class 子类:继承方式 父类1,继承方式 父类2......

        多继承可能会引发父类中有同名成员出现,需要加作用域区分

        C++实际开发中不建议用多继承

8)菱形继承

        概念:两个派生类继承同一个基类

                   又有某个类同时继承着两个派生类

                   这种继承被称为菱形继承,或者钻石继承

        带来的问题:

                        子类继承两份相同的数据,导致资源浪费以及毫无意义

菱形继承案例:

 在这个案例中会存在不知道继承谁的年龄,这时我们需要用到虚继承

        继承之前 加上关键字 virtual 变为虚继承//class 子类:virtual 继承类型 父类{};

        虚继承的类称为虚基类        

        虚继承会使两个要继承的结果系统自动变成一个唯一的结果

10.多态

1)基本概念:

        静态多态:函数重载和运算符重载属于静态多态,复用函数名

        动态多态:派生类和虚函数实现运行时多态

        区别:

        静态多态函数地址早绑定-编译阶段确定函数地址

        动态多态的函数抵制晚绑定-运行阶段确定函数地址

        将静态变成动态,需要再成员函数前面加上virtual

        动态多态的使用:

                父类的指针或者引用 指向子类对象

        重写:函数返回值类型 函数名 参数列表 完全一致的称为重写

2)纯虚函数和抽象类

        在多态中,通常父类中虚函数的实现是毫无意义的,主要都是调用子类重写的内容

        因此可以将虚函数改为纯虚函数

        语法:virtual 返回值类型 函数名(参数列表)=0;

        当类中有了纯虚函数,这个类也称为抽象类

抽象类特点:

        无法实例化对象

        子类必须重写抽象类中的纯虚函数,否则也属于抽象类        

3)案例一:计算器类

        输入两个类,进行运算处理

        普通写法

#include<iostream>
#include<string>
using namespace std;
class Calculator
{
public:
	int getResult(string oper)
	{
		if(oper=="+") 
		{
			return num1+num2;
		}
		else if(oper=="-")
		{
			return num1-num2;
		}
		else if(oper=="*")
		{
			return num1*num2;
		}
	}	
	int num1;//操作位1 
	int num2;//操作位2 
};
void test()
{
	Calculator c;
	c.num1=10;
	c.num2=10;
	cout<<c.num1<<"+"<<c.num2<<"="<<c.getResult("+")<<endl;
	cout<<c.num1<<"-"<<c.num2<<"="<<c.getResult("-")<<endl;
	cout<<c.num1<<"*"<<c.num2<<"="<<c.getResult("*")<<endl;
}
int main()
{
	test();
	system("pause");
	return 0;
} 

 弊端:如果想扩展新的功能,需求修改源码

            在真实的开发中 提倡 开闭原则

            开闭原则:对扩展进行开放,对修改进行关闭

利用多态技术改进

#include<iostream>
#include<string>
using namespace std;
class AbstractCalculator//实现计算器抽象类 
{
public:
	virtual int getResult()=0;//纯虚函数 
	int num1;
	int num2;
};
//加法 
class AddCalculator:public AbstractCalculator
{
public:
	virtual int getResult()
	{
		return num1+num2;
	}
};
//减法
class SubCalculator:public AbstractCalculator
{
public:
	virtual int getResult()
	{
		return num1-num2;
	}
};
//乘法
class MulCalculator:public AbstractCalculator
{
public:
	virtual int getResult()
	{
		return num1*num2;
	}
}; 
void test()
{
	//多态使用条件
	//加法
	AbstractCalculator *abc=new AddCalculator;
	abc->num1=100;
	abc->num2=100;
	cout<<abc->num1<<"+"<<abc->num2<<"="<<abc->getResult()<<endl;
	delete abc;//用完记得销毁
	//减法
	abc=new SubCalculator;
	abc->num1=100;
	abc->num2=100;
	cout<<abc->num1<<"-"<<abc->num2<<"="<<abc->getResult()<<endl;
	//乘法
	abc=new MulCalculator;
	abc->num1=100;
	abc->num2=100;
	cout<<abc->num1<<"*"<<abc->num2<<"="<<abc->getResult()<<endl;
}
int main()
{
	test();
	system("pause");
	return 0;
}

 多态好处:

        组织结构清晰

        可读性强

        对于前期和后期扩展以及维护性高

4)虚析构和纯虚析构

        多态使用时,如果子类中属性开辟的堆区,那么父类指针在释放时无法调用到子类的析构代码

        父类指针在析构时候 不会调用子类中析构函数,导致子类如果有堆区属性,出现内存泄漏

        解决方式:将父类中的析构函数改为虚析构或者纯虚析构

共性:

        可以解决父类指针释放子类对象

        都需要有具体的函数实现

区别:

        如果是纯虚析构,该类属于抽象类,无法实例化对象

        纯虚析构需要声明也需要实现,类外声明,类内实现

        虚析构语法:virtual ~类名(){}

        纯虚析构语法:类内:virtual ~类名()=0;

                                 类外:类名:: ~类名(){};

如果子类中没有堆区数据,可以不写虚析构或纯虚析构

虚析构案例:

#include<iostream>
#include<string>
using namespace std;
class Animal
{
public:
	Animal()
	{
		cout<<"Animal构造函数调用"<<endl;
	}
	virtual void speak()=0;
	virtual ~Animal()
	{
		cout<<"Animal析构函数调用"<<endl;
	}//虚析构 
	string *name;
};
class Cat:public Animal
{
public:
	Cat(string n)
	{
		name=new string(n);
		cout<<"Cat构造函数调用"<<endl;
	}
	void speak()
	{
		cout<<*name<<"小猫在说话"<<endl;
	}
	~Cat()
	{
		if (name!=NULL)
		{
			cout<<"Cat析构函数调用"<<endl;
			delete name;
			name=NULL;
		}
	}
	string *name;
};
void test()
{
	Animal *a=new Cat("Tom");
	a->speak();
	delete a;
}
int main()
{
	test();
	system("pause");
	return 0;
}

 纯虚析构:

#include<iostream>
#include<string>
using namespace std;
class Animal
{
public:
	Animal()
	{
		cout<<"Animal构造函数调用"<<endl;
	}
	virtual void speak()=0;
	virtual ~Animal()=0;//纯虚析构 
	string *name;
};
Animal::~Animal()
{
	
} //声明 
class Cat:public Animal
{
public:
	Cat(string n)
	{
		name=new string(n);
		cout<<"Cat构造函数调用"<<endl;
	}
	void speak()
	{
		cout<<*name<<"小猫在说话"<<endl;
	}
	~Cat()
	{
		if (name!=NULL)
		{
			cout<<"Cat析构函数调用"<<endl;
			delete name;
			name=NULL;
		}
	}
	string *name;
};
void test()
{
	Animal *a=new Cat("Tom");
	a->speak();
	delete a;
}
int main()
{
	test();
	system("pause");
	return 0;
}

5)电脑组装案例(多态大部分知识都包括在内)

        描述:

        电脑主要组成部分为CPU,显卡,内存

        将每个零件封装出抽象基类,并提供不同的厂商产生不同的零件,例如Intel和Lenovo

        创建电脑类提供让电脑工作的函数,并调用每个零件工作的接口

        测试时组装三台不同的电脑进行工作     

#include<iostream>
#include<string>
using namespace std;
//抽象CPU类 
class CPU
{
public:
 	//抽象的计算函数 
	virtual void calculate()=0;
};
//抽象显卡类 
class VideoCard
{
public:
	//抽象的显示函数 
	virtual void display()=0;
};
//抽象内存类 
class Memory 
{
public:
	//抽象的存储函数 
	virtual void storage()=0;
};
class Computer
{
public:
	Computer(CPU *cpu,VideoCard *vc,Memory *mem) 
	{
		m_cpu=cpu;
		m_vc=vc;
		m_mem=mem; 
	}
	//工作函数,让零件工作起来 
	void work()
	{
		m_cpu->calculate();
		m_vc->display();
		m_mem->storage();
	}
	//析构释放3个零件
	~Computer()
	{
		if(m_cpu!=NULL)	
		{
			delete m_cpu;
			m_cpu=NULL;
		}
		if(m_vc!=NULL)	
		{
			delete m_vc;
			m_vc=NULL;
		}
		if(m_mem!=NULL)	
		{
			delete m_mem;
			m_mem=NULL;
		}
	} 
private:
	CPU *m_cpu;//CPU的零件指针 
	VideoCard *m_vc;//显卡的零件指针 
	Memory *m_mem;//内存的零件指针 		
};
//Intel产商 
class IntelCPU:public CPU
{
public:
	virtual void calculate()
	{
		cout<<"Intel的CPU开始计算"<<endl;
	}
};
class IntelVideoCard:public VideoCard
{
public:
	virtual void display()
	{
		cout<<"Intel的显卡开始显示"<<endl;
	}
};
class IntelMemory:public Memory
{
public:
	virtual void storage()
	{
		cout<<"Intel的内存开始储存"<<endl;
	}
};
class LenovoCPU:public CPU
{
public:
	virtual void calculate()
	{
		cout<<"Lenovo的CPU开始计算"<<endl;
	}
};
class LenovoVideoCard:public VideoCard
{
public:
	virtual void display()
	{
		cout<<"Lenovo的显卡开始显示"<<endl;
	}
};
class LenovoMemory:public Memory
{
public:
	virtual void storage()
	{
		cout<<"Lenovo的内存开始储存"<<endl;
	}
};
void test()
{
	//零件创建
	CPU *intelCpu=new IntelCPU;
	VideoCard *intelCard=new IntelVideoCard;
	Memory *intelMem=new IntelMemory;
	//创建第一台电脑
	Computer *computer1=new Computer(intelCpu,intelCard,intelMem);
	computer1->work();
	delete computer1;
	cout<<"-------------------"<<endl;
	cout<<"第二台电脑开始工作"<<endl;
	//创建第二台电脑
	Computer *computer2=new Computer(new LenovoCPU,new LenovoVideoCard,new LenovoMemory);
	computer2->work();
	delete computer2; 
	cout<<"-------------------"<<endl;
	cout<<"第三台电脑开始工作"<<endl;
	//创建第三台电脑
	Computer *computer3=new Computer(new LenovoCPU,new IntelVideoCard,new LenovoMemory);
	computer3->work();
	delete computer3; 
}
int main()
{
	test();
	system("pause");
	return 0;	
} 

 结果为

 到这里c++中类和对象的内容就学完了,但有很多东西还需要自己去摸索,写出一个属于自己风格的代码

 注:部分图片来自b站黑马程序员的视频之中

  • 23
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值