对象的初始化分类
c++
拷贝构造函数
1.使用一个已经创建完毕得对象来初始化一个新对象
2.值传递得方式给函数参数传值
3.以值方式返回局部对象
构造函数调用规则
1.默认构造函数(无参。函数体空)
2.默认析构函数(无参。函数体空)
3.默认拷贝构造函数,对属性进行拷贝
深拷贝 浅拷贝
1.浅拷贝带来得问题就是堆区得内存重复释放
2.浅拷贝得问题要利用深拷贝进行解决。
总结:如果属性有在堆区开辟得,一定要自己提供拷贝构造函数,防止浅拷贝带来的问题。
class Person
{
public:
Person()
{
cout << "默认构造函数调用" << endl;
}
Person(int age,int height)
{
m_Age = age;
m_Height=new int(height);
cout << "有参默认构造函数调用" << endl;
}
Person(const Person& p)
{
cout << "拷贝构造函数" << endl;
m_Age = p.m_Age;
//深拷贝
m_Height=new int(*p.m_Height);
}
~Person()
{
cout << "析构函数调用" << endl;
}
int m_Age;//年龄
int* m_Height;
};
void test01()
{
Person p1(18 ,160);
cout << p1.m_Age <<" "<< *p1.m_Height<<endl;
Person p2(p1);
cout << p2.m_Age << endl;
}
int main()
{
test01();
system("pause");
return 0;
}
类对象作为类成员
class Phone
{
public:
Phone(string name)
{
m_PhoneName = name;
cout << "苹果构造" << endl;
}
~Phone()
{
cout << "手机xigo" << endl;
}
string m_PhoneName;
};
class Person
{
public:
Person(string name, string pName) :m_Name(name),m_Phone(pName)
{
}
string m_Name;
Phone m_Phone;
~Person()
{
cout << "调用" << endl;
}
void playGame()
{
cout << m_Name << "拿着:" <<m_Phone.m_PhoneName << endl;
}
};
void test01()
{
Person p("sb", "苹果");
p.playGame();
}
int main()
{
test01();
system("pause");
return 0;
}
静态成员
关键字 static
类外访问不到类内的静态成员函数
成员变量和成员函数分开存储
空对象占用内存空间为1;
c++编译器会给空对象分配一个字节空间,为了区分空对象占内存位置。即有独一无二地址
非静态成员变量属于类的对象上
静态成员变量不属于类的对象上
this指针的使用
this指针指向被调用的成员函数所属的对象
this->age= age;
1.解决名称冲突
2.返回对象本身用*this
(this指向P2的指针,而*this指向的就是p2这个对象本体)
空指针调用成员函数
健壮性
const修饰成员函数
const不可以修改成员属性
关键字mutable就可以 修改。
常对象只能调用常函数
在对象前加const就是常对象
mutable在常函数和常对象内都可以修改
友元
友元关键字 friend
友元的三种实现
1.全局函数做友元
2.类做友元
3.成员函数做友元
告诉编译器
friend+函数体
类做友元
友元函数可以访问本类中的私有成员
成员函数做友元
friend
运算符重载
1.加号运算符重载
2.左移动运算符重载
ostream &operator(ostream &cout,Person&p)
{
out<<xxxxxx
return out;
}
3.递增运算符重载
4.赋值运算符重载
继承
继承的好处就是减少重复的代码
class A: public B
A类成为子类 或派生类
B类成为父类或基类
派生类中的成员,包含两大部分
一类是从基类继承过来的,一类是自己增加的成员
从基类继承过来的表现性共性,新增的成员体现了其个性
继承的方式
语法: class 子类:继承 父类
继承的方式
1.公共继承
2.保护继承
3.私有继承
继承中的对象模型
在父类中所有非静态成员属性都会被子类继承下去
父类中私有成员属性被编译器隐藏,但是有继承的行为
利用开发人员命令提示工具查看对象模型
跳转符 F:
跳转文件路径 cd 具体路径
查看命名
c1 / d1 reportSingClassLayout类名
继承中构造和析构顺序
继承中的构造和析构顺序如下
先构造父类,再构造子类,析构的顺序和构造相反。.
继承同名成员处理方式
子类对象可以直接访问到子类中同名成员
子类对象加作用域可以访问父类同名成员
当子类和父类拥有同名的成员函数,子类会隐藏父类中同名成员函数,加作用域可以访问到父类中同名函数
x.父类::函数
继承同名静态成员处理方式
与上面一样
多继承语法
class 子类: 继承方式 父类1,继承方式 父类2…
菱形继承
利用虚继承 解决菱形继承的问题
在继承之前加上关键字virtual 变为虚继承
vbptr —vbtable
v-virtual
b-base
ptr-pointer
多态
多态基本概念
多态分为
静态多态:函数重载,运算符重载,复用函数名
动态多态: 派生类和虚函数是实现运行时多态
静态多态和动态多态区别
静态多态的函数地址早绑定-编译阶段确定函数地址
动态多态的函数地址晚绑定-运行阶段确定函数地址
虚函数virtual
动态多态满足条件
1.有继承条件
2.子类要重写父类的虚函数 重写 函数返回值类型 函数名 参数列表 相同
动态多态使用
父类的指针或者引用 执行子类对象
多态原理剖析
指针4个字节
当子类重写父类的虚函数
子类中的虚函数表内部会替换成子类的虚函数地址
当父类的指针或者引用指向子类对象时候,发生多态
Animal & animal=cat;
animal.speak();
多态的案例
多态的优点
代码清晰
可读性强
利于前期后期的拓展以及维护
纯虚函数和抽象类
在多态中,通常父类中虚函数的实现没有意义主要是调用子类重写内容 所以可以改为纯虚函数
纯虚函数 virtual 返回值类型 函数名 (参数列表)=0;
当类中有纯虚函数,这个类也成为抽象类
抽象类特点
无法实例化对象
子类必须重写抽象类中的虚函数,否则也属于抽象类
虚析构和纯虚构
多态使用时候,如果子类中属性开辟到堆区,那么父类指针在释放的时候无法调用子类的析构代码
解决办法:将父类中的析构函数改为虚析构或者纯虚构
虚析构和纯虚构共性
可以解决父类指针释放子类对象
都需要有具体的函数实现
虚析构和纯虚构区别
如果是纯虚构,该类是抽象类 无法实例化。
虚析构 virtual ~类名(){}
纯虚构 virtual~类名()=0;
类名::