C++构造函数的几个难点(基于C++ 11)

快要有一个月没有更新博客了,是时候再动一动笔啦!因为最近在学习C++,在学习过程中看了好多书,也在实际训练中遇到了一些问题。所以在接下来的时间里,应该会对C++里自己遇到的一些难点写几篇专题,就当是为自己梳理巩固知识啦!

我们都知道所谓构造函数就是类用来初始化各个数据成员的(非静态),如果成员都没初始化好,那么在对类的后续操作中肯定会遇到各种莫名其妙的错误。所以,我们有必要对构造函数做一个全面的了解,知道在各种情况下,构造函数都是怎样执行的,这样才能对类进行完美的控制。

默认构造函数:

class base{
public:
    base()=default;
    base(string& s1,int a1):s(s1),a(a1){}
    base(base& b):s(b.s),a(b.a){}
    void print(){cout<<s;cout<<a;}
private:
    const string s;
    int a=0;
};

首先我们都知道构造函数是支持重载的,一个类的对象可以支持多种初始化方式。那么如果类里面没有定义构造函数怎么办呢?其实无所谓,因为系统会自动对类里面的各个数据成员进行初始化。例如,类中有个string类型的变量s。系统就会自动调用string的构造函数,将s初始化为空字符串,对于其他的数据成员进行类似操作即可。需要注意的是,在C++ 11中是支持类的数据成员在声明是赋值的,因此当base类的对象采用默认构造方式是,成员a的值就为0。那么,如果在类中有构造函数呢?这个时候系统就不提供默认构造函数了,这个时候当你创建类的对象,并且不提供参数初始化的话,编译器就会报错。当然,假如你想要使用系统默认的构造函数也是可以的。如上代码所示,C++
11中只要在无参的构造函数名后面加=default就可以了。

初始化列表:

记得当初还没有认真学习C++的时候,总是疑惑这初始化列表到底是干嘛的?在函数体里面进行赋值不是一样么?事实上当然是不一样的。其实在构造函数的函数体之前,各个数据成员就都已经初始化好了。如果在函数体内有赋值语句,对,那是赋值,而不是初始化!这一点当存在const变量的时候表现得特别明显。因为const变量只能初始化而不能赋值。就像上面的代码中的s,当s在函数体内被赋值的话,系统就会报错。这就是初始化列表的意义。

单继承类的构造函数:

class derived:public base{
public:
    derived()=default;
    derived(derived& der1):base(der1),b(der1.b){}
private:
    int b=0;
};

在上面的代码中,derived类是base类的继承类。在类的继承当中有一个非常重要的原则,那就是自己干自己的事情。例如,在初始化继承类的过程中,继承类的父类部分,它是不管的,只是将参数传给父类的构造函数,让它自己去初始化好了,它要做的是初始化自己新增加的部分。那么问题就来了,在继承类的赋值构造函数中,继承类部分的赋值是没有问题的,但是基类部分怎么办呢?因为基类的赋值构造函数要求的是基类的对象,但是参数是继承类。如果想要对基类的成员一个一个赋值也是不现实的,因为继承类对象是不能访问基类部分的私有成员的。那怎么办呢?很简单,把继承类对象作为参数赋值给基类的赋值构造函数。虽然基类的赋值构造函数要求的参数是基类对象的引用,但是显然,继承类对象的包含基类的。因此,只要把继承类对象传递过去,系统会自动将基类部分完成初始化。对于继承类的构造函数,还有一点需要注意到是初始化的顺序。我们可以把整个类的继承看做一棵树,那么当前所在的继承类就可以看做是叶子节点。这样看来就很简单了,对于继承类的初始化过程就是从树根到树叶逐步初始化的过程。就如上面的代码,在初始化derived类的对象的时候,先初始化它的base部分,然后再初始化它自己新添加的部分。

多继承类的构造函数:

多继承类构造函数的运作方式其实和单继承类是类似的。不过有一点需要特别注意的是,当多继承类中存在虚基类的时候,为了防止虚基类被重复构造。我们再当前类的构造函数中就要对其进行初始化。例如,我们有如下继承关系:A->B1->C,A->B2->C。其中A是C的虚基类。如果我们还是按照单继承的方式进行初始化,显然,因为有两条路径,所以A会被初始化两次。所以我们在构造C的时候先要对虚基类进行初始化并且C的构造函数应采用如下格式:

C(...):A(...),B1(..),B2(...)...{ }

即使没有将A的构造函数显式声明,系统依然会先默认初始化A。并且由于多类继承从左往右的初始化顺序,这四个类将按照:A,B1,B2,C的顺序完成初始化。

以上这些内容就是我对C++构造函数中存在的难点进行的一些总结。

时间: 2024-10-02 06:30:41

C++构造函数的几个难点(基于C++ 11)的相关文章

基于C++11的线程池

1.封装的线程对象 class task : public std::tr1::enable_shared_from_this<task> { public: task():exit_(false){} task( const task & ) = delete; ~task(){} task & operator =( const task &) = delete; void start(); void stop() { exit_ = true; sync_.not

第一次作业:基于Linux0.11操作系统的进程模型分析

1.前言 本文基于Linux0.11操作系统的源代码,分析其进程模型. Linux0.11下载地址:https://zhidao.baidu.com/share/20396e17045cc4ce24058aa43a81bf7b.html 2.进程的定义 程序是一个可执行的文件,而进程(process)是一个执行中的程序实例. 进程和程序的区别: 几个进程可以并发的执行一个程序 一个进程可以顺序的执行几个程序 进程由可执行的指令代码.数据和堆栈区组成.进程中的代码和数据部分分别对应一个执行文件中的

基于C++11的call wrapper

要在C++中应用AOP,不像在其他的基于解释器的语言中那么方便,作为一种静态语言,如果给函数或者类的方法造一个wrapper,在wrapper里面嵌入调用前的代码和调用后的代码,也能达到一定程度的代码织入的效果.在C++11之前,要给一个函数或者方法做个能返回调用结果的wrapper并不简单,会比较复杂,而在C++11里面可以使用function模版,以及函数返回类型的推定,通过生成一个内嵌类的对象的构造函数来执行before动作,析构函数来实现after动作,而具体的before和action

基于c++11新标准开发一个支持多线程高并发的网络库

背景 新的c++11标准出后,c++语法得到了很多的扩展,比起以往任何时候都要灵活和高效,提高了程序编码的效率,为软件开发人员节省了不少的时间. 之前我也写过基于ACE的网络服务器框架,但ACE毕竟有些臃肿,内部对象关系错综复杂,容易给人造成只见树木不见森林的错觉. 所以打算用c++11开发一个较为简洁,高效,支持高并发的网络库. 开源         花了两三周,终于把基础的结构开发完成,代码也开源在github上,网址是 https://github.com/lichuan/fly 欢迎各位

C++ 概念总结(基于 C++11)

构造 构造函数形式: 默认构造函数 拷贝构造函数 定义: 当定义一个新对象并用一个同类型的对象对它进行初始化时,将显式使用复制构造函数 形式: A(const A& h){} 调用时机: 当对象作为参数传递时 赋值操作符: 定义: 赋值操作符可以通过制定不同类型的右操作数而重载. 形式: A& operator = (const A& h){} 调用时机: 当对象需要用 '=' 进行赋值时 移动构造函数 定义: 源对象资源的控制权全部交给目标对象 形式: A(A &&

基于C++11的线程池,简洁且可以带任意多的参数

咳咳.C++11 加入了线程库,从此告别了标准库不支持并发的历史.然而 c++ 对于多线程的支持还是比较低级,稍微高级一点的用法都需要自己去实现,譬如线程池.信号量等.线程池(thread pool)这个东西,在面试上多次被问到,一般的回答都是:"管理一个任务队列,一个线程队列,然后每次取一个任务分配给一个线程去做,循环往复." 貌似没有问题吧.但是写起程序来的时候就出问题了. 废话不多说,先上实现,然后再啰嗦.(dont talk, show me ur code !) 代码实现 1

基于C++11线程池

1.包装线程对象 class task : public std::tr1::enable_shared_from_this<task> { public: task():exit_(false){} task( const task & ) = delete; ~task(){} task & operator =( const task &) = delete; void start(); void stop() { exit_ = true; sync_.noti

一个基于C++11的单例模板类

1 #ifndef _SINGLETON_H_ 2 #define _SINGLETON_H_ 3 4 #include <mutex> 5 #include <memory> 6 7 template<typename T> 8 class Singleton { 9 public: 10 template <typename... ArgTypes> 11 static T* getInstance(ArgTypes... args) { 12 stat

基于vue-cli3.11.0创建创建vue项目

如果电脑已安装vue-cli2.9.6,需要先卸载,然后重装vue-cli3.11.0操作如下 等待几分钟,卸载完成,输入npm install -g @vue/cli 等待安装完成,输入vue -V查看脚手架版本号 下面开始创建项目 首先进入你指定的工作空间文件夹,输入vue create 项目名称 选择default,等待项目创建完成,输入cd 项目名称,进入项目,然后启动项目npm run serve 启动成功后,可通过url在浏览器中打开项目查看 浏览器打开: 原文地址:https://