宠物出售的网站怎么做,seo优化,实名网站空间,网站设计的规范目录
1.引言
2.默认构造函数
3.自定义构造函数
4.带继承关系类的构造函数
5.带多重继承关系类的构造函数
6.带虚继承关系类的构造函数
7.总结 1.引言 对于学过C的来说#xff0c;构造函数是非常熟悉不过的了。但是你真正了解它吗#xff1f;构造函数内部初始化变量的顺…目录
1.引言
2.默认构造函数
3.自定义构造函数
4.带继承关系类的构造函数
5.带多重继承关系类的构造函数
6.带虚继承关系类的构造函数
7.总结 1.引言 对于学过C的来说构造函数是非常熟悉不过的了。但是你真正了解它吗构造函数内部初始化变量的顺序是怎么样的多种继承构造函数的执行顺序等等。今天我从前两天编程时遇到的一个问题说起代码如下(只列举了部分关键代码)
#pragma once
#include QObject
#include memoryclass cCtkContext {};class CLinkCoreFactory
{
public:explicit CLinkCoreFactory(cCtkContext* pContext) : m_pContext(pContext) {}//...
private:cCtkContext* m_pContext;
};class IDataNotify
{
public:virtual void notify() 0;//...
};class CThirdIInterface
{
public:explicit CThirdIInterface(CLinkCoreFactory* pFactory): m_pFactory(pFactory) {}//...private:CLinkCoreFactory* m_pFactory;
};class CDeviceManager : public QObject, public IDataNotify, public CThirdIInterface
{
public:CDeviceManager(cCtkContext* pContext, QObject* parent nullptr): QObject(parent), m_linkCoreFactory(new CLinkCoreFactory(pContext)), CThirdIInterface(m_linkCoreFactory.get()){}
public:void notify() override {}//...private:std::unique_ptrCLinkCoreFactory m_linkCoreFactory;
};int main()
{cCtkContext context;CDeviceManager manager(context);return 0;
} 在CDeviceManager内部定义了m_linkCoreFactoryCDeviceManager又继承于CThirdIInterfaceCThirdIInterface又依赖于CLinkCoreFactory于是CDeviceManager构造的时候在写法上先构造了变量m_linkCoreFactory再构造类CThirdIInterface但是调试运行的时候最后发现CThirdIInterface中的m_pFactory是无个效指针为什么会这样呢下面就这个问题深入讲解一下。
2.默认构造函数 如果类中没有定义任何构造函数编译器会自动提供一个默认构造函数这个构造函数不接受任何参数并且不进行任何初始化操作对象内的成员变量将使用它们的默认构造函数进行初始化对于内置类型这通常意味着它们将不会被初始化。但是一旦定义了任何构造函数包括带参数的构造函数编译器将不再自动生成默认构造函数。如果你需要默认构造函数你必须显式地定义它。 示例如下
#include iostream
using namespace std; class Box { public: //使用default关键字定义默认构造函数length、breadth、height不会初始化Box()default;//也可以自定义构造函数初始化length、breadth、height
#if 0Box() : length(0.0),breadth(0.0),height(0.0) {}
#endif //也可以自定义带参数的构造函数初始化length、breadth、heightBox(double len, double bre, double hei): length(len), breadth(bre), height(hei) { } // 成员函数来计算盒子的体积 double getVolume(void) { return length * breadth * height; } private:double length; // 长度 double breadth; // 宽度 double height; // 高度
}; int main() { Box Box1(3.3, 3.3, 3.3); // 声明 Box1 为 Box 类的一个对象 // Box1 的三个边初始化为 3.3 // 输出 Box1 的体积 cout Volume of Box1 : Box1.getVolume() endl; return 0;
}
在Box内部定义了一个默认构造函数可以定义多个自定义构造函数即是说构造函数是可以重载的。
3.自定义构造函数 在C中自定义构造函数允许你为类的实例提供特定的初始化逻辑。构造函数初始化列表是构造函数体之前的一个冒号:后跟一个以逗号分隔的初始化器列表。它用于初始化成员变量特别是那些不能通过赋值来初始化的成员如const成员、引用成员或没有默认构造函数的类类型的成员。 上面示例中的
Box(double len, double bre, double hei): length(len), breadth(bre), height(hei) { }
就属于自定义构造函数。
4.带继承关系类的构造函数 在C中子类派生类不能直接继承父类基类的构造函数。但是子类可以定义自己的构造函数并在其构造函数内部调用父类的构造函数如果父类有可访问的非私有构造函数以进行必要的初始化。这是通过使用成员初始化列表来实现的在初始化列表中可以显式地调用父类的构造函数。 以下是一个简单的示例展示了如何在带有继承的C类中处理构造函数
#include iostream
using namespace std; // 基类
class Base {
public: // 基类的构造函数 Base(int val) : a(val) { cout Base class constructor called, a a endl; } // 基类的另一个构造函数可选 Base() : a(0) { cout Base class default constructor called, a a endl; }
private:int a;
}; // 派生类
class Derived : public Base {
public: // 派生类的构造函数显式调用基类的构造函数 Derived(int val1, int val2) : Base(val1), b(val2) { cout Derived class constructor called, b b endl; } // 如果需要也可以定义默认构造函数不显式调用Base的构造函数时会调用Base的默认构造函数 Derived() : Base(), b(0) { cout Derived class default constructor called, b b endl; }
private:int b;
}; int main() { // 使用自定义构造函数创建Derived对象 Derived obj1(5, 10); // 使用默认构造函数创建Derived对象同时也会调用Base的默认构造函数 Derived obj2; return 0;
} 在上面的代码中Derived类继承自Base类。Derived类有两个构造函数一个接受两个参数一个用于Base类一个用于自身另一个是无参数的默认构造函数。在Derived的构造函数中通过成员初始化列表显式地调用了Base类的构造函数。 如果Base类没有可访问的默认构造函数并且你试图在Derived类中定义一个不接受任何参数的构造函数而不显式调用Base类的某个构造函数那么编译器会报错因为它不知道应该使用Base类的哪个构造函数。 此外构造函数初始化列表是初始化成员变量的首选方式因为它比在构造函数体内赋值更高效特别是对于常量成员、引用成员以及没有默认构造函数的类类型成员。
注意事项
•初始化顺序派生类的构造函数首先初始化基类部分然后初始化派生类自己的成员变量。这意味着即使你在派生类构造函数体内调用基类构造函数基类部分也已经在调用之前被初始化了。
5.带多重继承关系类的构造函数 在C中多重继承允许一个类派生类同时从多个基类继承属性和行为。在处理多重继承时派生类的构造函数需要负责初始化所有基类以及自身的成员变量。这通常是通过构造函数初始化列表来完成的其中可以明确指定如何调用各个基类的构造函数。 下面是一个C多重继承的例子展示了如何在构造函数中初始化多个基类
#include iostream
using namespace std; // 第一个基类
class Base1 {
public: Base1(int val) : a(val) { cout Base1 constructor called, a a endl; }
private:int a;
}; // 第二个基类
class Base2 {
public: Base2(int val) : b(val) { cout Base2 constructor called, b b endl; }
private:int b;
}; // 派生类继承自Base1和Base2
class Derived : public Base1, public Base2 {
public: // 派生类的构造函数 // 必须显式调用所有基类的构造函数 Derived(int val1, int val2, int val3) : Base1(val1), Base2(val2), c(val3) { cout Derived constructor called, c c endl; }
private:int c;
}; int main() { // 创建Derived类的实例 Derived obj(1, 2, 3); return 0;
} 在这个例子中Derived类同时从Base1和Base2两个基类继承。Derived类的构造函数通过构造函数初始化列表显式地调用了Base1和Base2的构造函数并传递了相应的参数。同时它也初始化了自己的成员变量c。 需要注意的是构造函数初始化列表中基类的调用顺序与它们在派生类声明中的顺序相同而不是与它们在初始化列表中出现的顺序相同。在这个例子中Base1的构造函数将在Base2的构造函数之前被调用尽管在初始化列表中Base2的调用出现在Base1之前。 此外如果基类中有默认构造函数即不接受任何参数的构造函数并且派生类的构造函数没有显式调用任何基类构造函数那么将隐式调用基类的默认构造函数如果存在的话。但是如果基类没有可访问的默认构造函数并且派生类的构造函数没有显式调用基类的某个构造函数那么编译器将报错。 现在我们回顾一下在文章开头的例子调试发现CThirdIInterface中的m_pFactory是无个效指针原因就是CDeviceManager的构造函数
CDeviceManager(cCtkContext* pContext, QObject* parent nullptr): QObject(parent), m_linkCoreFactory(new CLinkCoreFactory(pContext)), CThirdIInterface(m_linkCoreFactory.get()){}
CThirdIInterface构造函数的调用实际先于m_linkCoreFactory的构造导致传入CThirdIInterface构造函数的指针pFactory是空的。
6.带虚继承关系类的构造函数 虚继承Virtual Inheritance用于解决多重继承中可能出现的菱形继承Diamond Inheritance问题即一个类从多个基类继承而这些基类又共同继承自同一个基类导致基类在派生类中被多次实例化的问题。通过虚继承可以确保基类在继承体系中只被实例化一次。 在虚继承中派生类的构造函数依然需要负责初始化所有基类包括虚基类和其他非虚基类以及自身的成员变量。但是与常规继承不同的是虚基类的构造函数只在最底层的派生类中被调用一次且调用顺序由其在继承体系中的深度决定从最顶层的基类开始向下直到最底层的派生类。 示例如下
#include iostream
using namespace std; // 虚基类
class VirtualBase {
public: VirtualBase() { cout VirtualBase constructor called endl; }
}; // 第一个基类虚继承自VirtualBase
class Base1 : virtual public VirtualBase {
public: Base1() { cout Base1 constructor called endl; }
}; // 第二个基类也虚继承自VirtualBase
class Base2 : virtual public VirtualBase {
public: Base2() { cout Base2 constructor called endl; }
}; // 派生类同时继承自Base1和Base2
class Derived : public Base1, public Base2 {
public: Derived() { cout Derived constructor called endl; }
}; int main() { // 创建Derived类的实例 Derived obj; return 0;
}
在这个例子中Derived类从Base1和Base2继承而Base1和Base2都虚继承自VirtualBase。当你创建一个Derived类的实例时构造函数的调用顺序如下
1) VirtualBase的构造函数首先被调用因为VirtualBase是最顶层的基类并且只会被实例化一次。
2) 接着是Base1的构造函数注意尽管Base1虚继承自VirtualBase但VirtualBase的构造函数已经在前面被调用了所以这里不会再次调用它。
3) 然后是Base2的构造函数同样VirtualBase的构造函数不会被重复调用。
4) 最后是Derived的构造函数。
输出将是
VirtualBase constructor called
Base1 constructor called
Base2 constructor called
Derived constructor called
注意事项
•构造函数的调用顺序在虚继承中最派生类的构造函数负责首先调用虚基类的构造函数然后是其他非虚基类的构造函数。
•初始化责任由于虚基类只被初始化一次因此最派生类负责调用虚基类的构造函数。
7.总结 构造函数是C中非常重要的概念它们用于在对象创建时初始化对象的成员变量。构造函数没有返回类型并且其名称必须与类名相同。通过构造函数可以确保对象在创建时处于有效的状态。