复习
0.1 构造函数
对象被创建的时候,会自动调用
全局对象
局部对象
堆中的对象
构造函数的函数名字和类名一样
构造函数没有返回值,可以有参数,可以重载,一个类中可以实现多个构造函数
0.2 析构函数
对象被销毁的时候,会自动调用
全局对象 程序结束的时候会自动销毁
局部对象 离开作用域的时候,会自动销毁
堆中的对象 delete释放的时候,会自动销毁
析构函数名字:~ 类名
析构没有返回值,没有参数,不能重载,一个类就只能有1个
0.3 拷贝构造函数
一个对象初始化另外一个对象的时候,会自动调用
class CTest
{
//......
private:
int m_nNum;
}
1. 直接初始化
int main()
{
CTest obj1;
CTest obj2 = obj1;
}
2. 函数传参的时候
void Fun(CTest obj)
{
}
Fun(obj1);
编译器默认会提供一个浅拷贝的拷贝构造。
当类中成员,有指针指向堆空间的时候,我们大概率需要自己实现一个深拷贝的拷贝构造
0.4 转换构造函数
只有一个参数的构造函数,也称之为转换构造函数。
这个语法容易造成误解,可以使用explicit 禁止转换
1. 继承
1.1 继承的基本语法
#include <iostream>
class CA
{
public:
CA() :m_a(1)
{
}
void Fun1()
{
std::cout << m_a;
}
public:
int m_a;
};
//继承的语法:
//class 子类:public 父类的名字
class CB:public CA
{
public:
CB() :m_b(2)
{
}
void Fun2()
{
std::cout << m_b;
}
public:
int m_b;
};
int main()
{
std::cout << sizeof(CB)<<std::endl;
CB objB;
objB.Fun1();//这个函数从CA继承而来
objB.Fun2();//自己实现的
objB.m_a = 100;//使用的从CA继承来的
objB.m_b = 200;//自己定义的
return 0;
}
1.3 继承的权限
子类包含父类中的所有成员
子类也可以使用父类中的成员
在使用类的成员的时候,有两种情况:
1.通过类对象,在类外使用成员变量和成员函数
2.在类内的函数中,直接使用成员变量和成员函数
#include <iostream>
class CBase
{
public:
int m_BaseA;
void FunCBase()
{
std::cout << "我是CBase的FunCBase" << std::endl;
}
protected:
int m_BaseB;
private:
int m_BaseC;
};
class CA :public CBase
{
public:
void FunCA()
{
m_BaseA = 10;
m_BaseB = 20;
//m_BaseC = 5; 父类中的私有成员,无论如何继承都不能直接访问
}
};
class CB :protected CBase
{
public:
void FunCB()
{
m_BaseA = 10;
m_BaseB = 20;
//m_BaseC = 5; 父类中的私有成员,无论如何继承都不能直接访问
}
};
class CC :private CBase
{
public:
void FunCC()
{
m_BaseA = 10;
m_BaseB = 20;
//m_BaseC = 5; 父类中的私有成员,无论如何继承都不能直接访问
}
};
class CTest1 :public CB
{
public:
void FunTest1()
{
m_BaseA = 10;
m_BaseB = 20;
}
};
class CTest2 :public CC
{
public:
void FunTest2()
{
//m_BaseA = 10;
//m_BaseB = 20;
}
};
int main()
{
CA objA;
CB objB;
CC objC;
//CA是公有继承,公有继承也称之为 【接口继承】
//父类中是公有的,在子类中还是公有的,接口依然是接口
objA.m_BaseA = 1;
objA.FunCBase();
//私有继承或者保护结成也称之为 【实现继承】
//会使得父类中的公有成员变为私有的或者保护的
//子类就失去了父类中的接口,变成了内部实现。
//objB.m_BaseA = 2;
//objB.FunCBase();
//objC.m_BaseA = 2;
//objC.FunCBase();
//保护继承和私有继承,都是实现继承,有什么区别呢???
//私有继承,原公有或者保护成员,
//在子类中都变成了私有成员,再往下继承,就会不可访问
//保护继承,原公有或者保护成员,
//在子类中都是保护属性,再往下继承,类内还是能访问的
return 0;
}
1.4 重定义问题
父类中和子类中,只要重名了,就会发生重定义。通过子类对象访问同名成员,默认访问的都是子类自己的。父类中的同名成员被隐藏了。
无论是函数名和函数名同名,还是变量名和变量名同名,还是变量名和函数名同名。函数名同名,无论参数是一样还是不一样,都是重定义。父类中的标识符,都会隐藏,默认使用子类自己的。
注意:父类和子类中同名函数,不能构成重载的,因为他们作用域不同
#include <iostream>
class CBase
{
public:
void Fun()
{
std::cout << "CBase的Fun" << std::endl;
}
void Fun1()
{
}
int m_BaseA;
};
class CA :public CBase
{
public:
void Fun1(int n)
{
}
int m_BaseA;
int Fun;
};
int main()
{
CA obj;
obj.Fun = 100;
obj.Fun();
obj.Fun1();
obj.m_BaseA = 10;
return 0;
}
1.5 在继承关系中构造析构调用问题
#include <iostream>
class CTest1
{
public:
CTest1(int n) :m_Test1(n)
{
std::cout << "我是Test1成员构造" << std::endl;
}
~CTest1()
{
std::cout << "我是Test1成员析构" << std::endl;
}
public:
int m_Test1;
};
class CTest2
{
public:
CTest2(int m) :m_Test2(m)
{
std::cout << "我是Test2成员构造" << std::endl;
}
~CTest2()
{
std::cout << "我是Test2成员析构" << std::endl;
}
public:
int m_Test2;
};
class CBase1
{
public:
CBase1(int n) :m_BaseA(n)
{
std::cout << "我是1父类构造" << std::endl;
}
~CBase1()
{
std::cout << "我是1父类析构" << std::endl;
}
int m_BaseA;
};
class CBase2
{
public:
CBase2(int m) :m_BaseA(m)
{
std::cout << "我是2父类构造" << std::endl;
}
~CBase2()
{
std::cout << "我是2父类析构" << std::endl;
}
int m_BaseA;
};
class CA :public CBase1,public CBase2
{
public:
CA() : obj2(20),m_A(2), obj1(10), CBase2(6), CBase1(5)
{
std::cout << "我是子类构造" << std::endl;
}
~CA()
{
std::cout << "我是子类析构" << std::endl;
}
CTest1 obj1;
CTest2 obj2;
int m_A;
};
int main()
{
CA obj;
return 0;
}
总结:
1.构造优先调用父类,再调用成员,最后调用自己
2.析构顺序和构造顺序相反
3.父类构造需要传参,就在子类的初始化列表中主动调用父类的构造传参
4.成员的构造需要传参,也需要在类的初始化列表中主动调用成员的构造传参
5.成员的构造顺序和定义顺序一致
6.父类的构造顺序和继承顺序一致
1.6 多继承的问题
#include <iostream>
class CBase1
{
public:
CBase1(int n) :m_Base1(n)
{
std::cout << "我是1父类构造" << std::endl;
}
~CBase1()
{
std::cout << "我是1父类析构" << std::endl;
}
int m_Base1;
};
class CBase2
{
public:
CBase2(int m) :m_Base2(m)
{
std::cout << "我是2父类构造" << std::endl;
}
~CBase2()
{
std::cout << "我是2父类析构" << std::endl;
}
int m_Base2;
};
class CA :public CBase1, public CBase2
{
public:
CA() : m_A(2), CBase2(6), CBase1(5)
{
std::cout << "我是子类构造" << std::endl;
}
~CA()
{
std::cout << "我是子类析构" << std::endl;
}
public:
int m_A;
};
int main()
{
//子类多继承的时候,和单继承差不多的,拥有所有父类的所有成员
CA obj;
obj.m_Base1 = 10; //从CBase1继承来的
obj.m_Base2 = 20; //从CBase2继承来的
obj.m_A = 30; //这个是它自己的
return 0;
}
具体示例:
#include <iostream>
class Cpeople
{
public:
void Run()
{
std::cout << "我在快乐的跑步" << std::endl;
}
public:
char m_szName[20];
int m_nGender;
int nAge;
};
class CSpider//蜘蛛
{
public:
void Tusi()
{
std::cout << "Piu" << std::endl;
}
public:
int nAge;
};
class CSuperHero
{
public:
void SaveWorld()
{
std::cout << "拯救世界" << std::endl;
}
int nAge;
};
class CSPiderMan :public Cpeople, public CSpider,public CSuperHero
{
public:
void BianShen()
{
std::cout << "Piu" << std::endl;
}
};
int main()
{
CSuperHero obj1;
obj1.SaveWorld();
CSPiderMan obj;
obj.Run();
obj.Tusi();
obj.BianShen();
obj.SaveWorld();
//注意多继承的时候,多个父类如果有同名成员,会造成二义性问题。
//可以使用作用域防止BUG产生
//更好的方式是使用虚继承
obj.CSpider::nAge = 5;
obj.Cpeople::nAge = 20;
obj.CSuperHero::nAge = 15;
return 0;
}
2. 命名空间
命名空间机制是为了防止命名冲突
math1.h
#pragma once
namespace Code1
{
int GetMax(int a, int b);
int GetAdd(int a, int b);
}
math1.cpp
namespace Code1
{
int GetMax(int a, int b)
{
if (a > b)
{
return a;
}
else
{
return b;
}
}
int GetAdd(int a, int b)
{
return a + b;
}
}
math2.h
#pragma once
namespace Code2
{
int GetMax(int a, int b);
int GetAvg(int a, int b);
namespace inner
{
int GetMin(int a, int b);
}
}
math2.cpp
namespace Code2
{
int GetMax(int a, int b)
{
if (a < b)
{
return b;
}
else
{
return a;
}
}
}
namespace Code2
{
int GetAvg(int a, int b)
{
return (a + b) / 2;
}
namespace inner
{
int GetMin(int a, int b)
{
if (a < b)
{
return a;
}
else
{
return b;
}
}
}
}
主文件
#include "Math1.h"
#include "Math2.h"
using Code1::GetMax;
using Code2::GetAvg;
namespace mi = Code2::inner;
int main()
{
GetMax(10, 20);
GetAvg(10, 15);
mi::GetMin(5, 10);
return 0;
}
补充作业:
1.设计一个狼人类,继承自人类和狼类。
a.人类:学习方法 有年龄属性 有参构造进行初始化
b.狼类:攻击方法 有奔跑速度属性 有参构造进行初始化
c.狼人类:变身方法 有攻击力属性 有参构造进行初始化
定义正确定义狼人对象,能够调用继承过来的所有函数
#include<iostream>
using namespace std;
class people
{
public:
people(int age) :m_age(age) //构造函数使用
{
}
void study()
{
cout << "i am study now" << endl; //学习方法
}
private:
int m_age;
};
class wolf
{
public:
wolf(int speed) :m_speed(speed) //构造函数使用
{
}
void attack()
{
cout << "i am fighting here" << endl;
}
private :
int m_speed;
};
class wolfman :public people, public wolf //多继承,继承多个成员的属性
{
public:
wolfman(int power,int age, int speed):power(power),people(age),wolf(speed)//构造函数,用于初始化成员
{
}
void bianshen()
{
cout << "update!" << endl;
}
private:
int power; //初始化新定义的power
};
int main()
{
wolfman obj(100, 20, 70);
obj.study();
obj.attack();
obj.bianshen();
return 0;
}
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。