默认构造函数
分类
无参构造函数
移动构造函数
拷贝构造函数
可以使用=delete 来去除默认默认构造函数
可以使用=default 来创建某个默认构造函数
存在条件
类并不是一定会生成默认构造函数,必须满足一些条件的类才会生成默认构造函数
没有声明构造函数(必要条件)
以下条件必须存在一个,才会有默认构造函数
- 含有类对象数据成员,该类对象类型有默认构造函数
- 基类带有默认构造函数的派生类
- 带有虚函数的类
有虚函数,就一定有虚表指针,虚表指针必须在构造函数中指定,所以有默认构造函数 - 继承自虚基类的类
使用class X1:public virtual X2 方式继承的类
在菱形继承中,即使继承了多次X2,但只保留一个X2的指针,这个指针的设置必须在构造函数中指定,所以有默认构造函数
构造函数的执行顺序
- 父类构造
- 子类构造
测试:
class A
{
public:
A()
{
std::cout << "A 构造" << std::endl;
}
}
class C : public A
{
public:
C()
{
std::cout << "C 构造" << std::endl;
}
}
C *c = new C();
delete c;
/**
A 构造
C 构造
*/
析构函数的执行过程
- 子类析构
- 父类析构
测试:
class A
{
public:
~A()
{
std::cout << "A delete" << std::endl;
}
};
class C : public A
{
public:
~C()
{
std::cout << "C delete" << std::endl;
}
};
C *c = new C();
delete c;
/**
C delete
A delete
*/
多态
虚函数
类的多态是通过虚函数来实现的
使用方法: 使用virtual修饰函数;
- 按照运行时类型调用
- 虚函数的virtual可以在子类中被继承,不用在子类方法中添加virtual关键字
测试:
class A
{
public:
virtual void func2() // 演示虚函数的作用,根据运行时类型调用
{
std::cout << "A func2" << std::endl;
}
};
class C : public A
{
public:
virtual void func2()
{
std::cout << "C func2" << std::endl;
}
};
C *c = new C();
A *a = dynamic_cast<A *>(c);
a->func2();
c->func2();
delete c;
/**
C func2
C func2
*/
纯虚函数
使用方法: virtual func=0;
- 包含纯虚函数的类称为抽象类,不能实例化
- 纯虚函数在子类中必须被重写
测试:
class A
{
public:
virtual void func1() = 0; // 演示子类必须重写纯虚函数
};
class C : public A
{
public:
virtual void func1()
{
std::cout << "C func1" << std::endl;
}
};
C *c = new C();
A *a = dynamic_cast<A *>(c);
a->func1();
delete c;
/**
C func1
*/
虚析构
使用方法:virtual声明析构函数
如果不用虚析构,子类对象指针赋值给父类指针,delete 父类指针,那么按照编译时类型方式调用就会调用父类的析构函数,而不会调用子类的析构函数了,所以最好使用虚析构的方式来修饰析构函数
测试:
class A
{
public:
virtual ~A()
{ // 演示虚析构函数的作用
std::cout << "A delete" << std::endl;
}
};
class C : public A
{
public:
~C()
{
std::cout << "C delete" << std::endl;
}
};
C *c = new C();
A *a = dynamic_cast<A *>(c);
delete a;
/**
不使用virtual修饰~A()
A delete
使用virtual修饰~A()
C delete
A delete
*/
子类方法中调用父类函数
使用方法:在子类方法中使用父类::func的形式来调用父类函数
测试:
class A
{
public:
virtual void func4()
{ // 演示在子类中调用父类函数
std::cout << "A func4" << std::endl;
}
};
class C : public A
{
public:
virtual void func4()
{
std::cout << "C func4" << std::endl;
A::func4();
}
};
C *c = new C();
c->func4();
delete a;
/**
C func4
A func4
*/
非虚函数
使用方法:方法中不用virtual修饰,按照编译时类型调用
测试:
class A
{
public:
void func3()
{ // 演示非虚函数的作用,根据编译时类型调用
std::cout << "A func3" << std::endl;
}
};
class C : public A
{
public:
void func3()
{
std::cout << "C func3" << std::endl;
}
};
C *c = new C();
A *a = dynamic_cast<A *>(c);
a->func3();
c->func3();
delete a;
/**
A func3
C func3
*/
父类非虚方法中调用子类虚函数
使用方法: 在父类非虚方法中使用this->func的形式来调用子类虚函数
测试:
class A
{
public:
virtual void func1() = 0; // 演示子类必须重写纯虚函数
virtual void func2() // 演示虚函数的作用,根据运行时类型调用
{
std::cout << "A func2" << std::endl;
}
void func5()
{ // 演示在父类对象中调用子类中重写的虚函数
std::cout << "A func5" << std::endl;
this->func1();
this->func2();
}
};
class C : public A
{
public:
virtual void func1()
{
std::cout << "C func1" << std::endl;
}
virtual void func2()
{
std::cout << "C func2" << std::endl;
}
};
C *c = new C();
A *a = dynamic_cast<A *>(c);
a->func5();
delete c;
/**
A func5
C func1
C func2
*/
菱形继承
继承关系:
C->B1->A
C->B2->A
虚基类继承
原因:C中包含了两个A对象,当使用A的函数或变量时会出现不明确使用哪个A对象的问题,所以需要使用继承虚基类的方式来保证菱形继承的情况下同一个父类对象只有一个
使用方法:使用public virtual 父类的方式来创建子类
测试:
class A
{
public:
int x;
};
class B1 : public virtual A //虚基类继承
{
public:
};
class B2 : public virtual A //虚基类继承
{
public:
};
class C : public B1, public B2
{
public:
};
C *c = new C();
c->x = 1; // 如果不是继承自虚基类,这里会提示不明确
delete c;