首页 > 编程语言 >c++ 构造函数

c++ 构造函数

时间:2024-05-29 15:43:45浏览次数:25  
标签:初始化 int 成员 c++ Base public 构造函数

 

=================

1.简介
子类为完成基类初始化,在 C++11 之前,需要在初始化列表调用基类的构造函数,从而完成构造函数的传递。如果基类拥有多个构造函数,那么子类也需要实现多个与基类构造函数对应的构造函数。

class Base {
public:
Base(int v): _value(v), _c(‘0’){}
Base(char c): _value(0), _c(c){}
private:
int _value;
char _c;
};

class Derived: public Base {
public:
// 初始化基类需要透传参数至基类的各个构造函数,非常麻烦
Derived(int v) :Base(v) {}
Derived(char c) :Base(c) {}

// 假设派生类只是添加了一个普通的函数
void display() {
// dosomething
}
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
书写多个派生类构造函数只为传递参数完成基类初始化,这种方式无疑给开发人员带来麻烦,降低了编码效率。从 C++11 开始,推出了继承构造函数(Inheriting Constructor),使用 using 来声明继承基类的构造函数,我们可以这样书写。

class Base {
public:
Base(int v) :_value(v), _c('0'){}
Base(char c): _value(0), _c(c){}
private:
int _value;
char _c;
};

class Derived: public Base {
public:
// 使用继承构造函数
using Base::Base;

// 假设派生类只是添加了一个普通的函数
void display() {
// do something
}
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
我们通过 using Base::Base 把基类构造函数继承到派生类中,不再需要书写多个派生类构造函数来完成基类的初始化。

更为巧妙的是,C++11 标准规定,继承构造函数与类的一些默认函数(默认构造、析构、拷贝构造函数等)一样,是隐式声明,如果一个继承构造函数不被相关代码使用,编译器不会为其产生真正的函数代码。这样比通过派生类构造函数“透传构造函数参数”来完成基类初始化的方式,总是需要定义派生类的各种构造函数更加节省目标代码空间。

2.注意事项
(1)继承构造函数无法初始化派生类数据成员。

这个很好理解,因为继承构造函数的功能是初始化基类,对于派生类数据成员的初始化则无能为力。解决的办法主要有两个:

一是使用 C++11 特性就地初始化成员变量,可以通过 =、{} 对非静态成员快速就地初始化,以减少多个构造函数重复初始化变量的工作,注意初始化列表会覆盖就地初始化操作。

class Derived: public Base {
public:
// 使用继承构造函数
using Base::Base;

// 假设派生类只是添加了一个普通的函数
void display() {
// do something
}
private:
// 派生类新增数据成员
double _double{0.0};
};
1
2
3
4
5
6
7
8
9
10
11
12
13
二是新增派生类构造函数,使用构造函数初始化列表。

class Derived :public Base {
public:
// 使用继承构造函数
using Base::Base;

// 新增派生类构造函数
Derived(int a, double b):Base(a), _double(b){}

// 假设派生类只是添加了一个普通的函数
void display() {
// do something
}
private:
// 派生类新增数据成员
double _double{0.0};
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
相比之下,第二种方法需要新增构造函数,明显没有第一种方法简洁,但第二种方法可由用户控制初始化值,更加灵活。各有优劣,两种方法需结合具体场景使用。

(2)构造函数拥有默认值会产生多个构造函数版本,且继承构造函数无法继承基类构造函数的默认参数,所以我们在使用有默认参数构造函数的基类时必须要小心。

class A {
public:
A(int a = 3, double b = 4): _a(a), _b(b){}
void display() {
cout<<_a<<" "<<_b<<endl;
}

private:
int _a;
double _b;
};

class B:public A {
public:
using A::A;
};
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
那么 A 中的构造函数会有下面几个版本:

A()
A(int)
A(int, double)
A(const A&)
1
2
3
4
那么 B 中对应的继承构造函数将会有如下几个版本:

B()
B(int)
B(int, double)
B(const B&)
1
2
3
4
可以看出,参数默认值会导致多个构造函数版本的产生,因此在使用时需格外小心。

(3)多继承的情况下,继承构造函数会出现“冲突”的情况,因为多个基类中的部分构造函数可能导致派生类中的继承构造函数的函数签名(函数名与参数)相同。

class A {
public:
A(int i){}
};

class B {
public:
B(int i){}
};

class C : public A, public B {
public:
using A::A;
using B::B; //编译出错,重复定义C(int)

// 显示定义继承构造函数 C(int)
C(int i):A(i),B(i){}
};

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
为避免继承构造函数冲突,可以通过显示定义来阻止隐式生成的继承构造函数。

此外,使用继承构造函数还需要注意:如果基类构造函数被申明为私有成员函数,或者派生类是从虚基类继承而来 ,那么就不能在派生类中申明继承构造函数
————————————————

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。

原文链接:https://blog.csdn.net/K346K346/article/details/81703914

=========

构造函数初始化列表以一个冒号开始,接着是以逗号分隔的数据成员列表,每个数据成员后面跟一个放在括号中的初始化式。例如:

class CExample {
public:
    int a;
    float b;
    //构造函数初始化列表
    CExample(): a(0),b(8.8)
    {}
    //构造函数内部赋值
    CExample()
    {
        a=0;
        b=8.8;
    }
};

上面的例子中两个构造函数的结果是一样的。上面的构造函数(使用初始化列表的构造函数)显式的初始化类的成员;而没使用初始化列表的构造函数是对类的成员赋值,并没有进行显式的初始化。

初始化和赋值对内置类型的成员没有什么大的区别,像上面的任一个构造函数都可以。对非内置类型成员变量,为了避免两次构造,推荐使用类构造函数初始化列表。但有的时候必须用带有初始化列表的构造函数:

  • 1.成员类型是没有默认构造函数的类。若没有提供显示初始化式,则编译器隐式使用成员类型的默认构造函数,若类没有默认构造函数,则编译器尝试使用默认构造函数将会失败。
  • 2.const 成员引用类型的成员。因为 const 对象或引用类型只能初始化,不能对他们赋值。

初始化数据成员与对数据成员赋值的含义是什么?有什么区别?

首先把数据成员按类型分类并分情况说明:

  • 1.内置数据类型,复合类型(指针,引用)- 在成员初始化列表和构造函数体内进行,在性能和结果上都是一样的
  • 2.用户定义类型(类类型)- 结果上相同,但是性能上存在很大的差别。因为类类型的数据成员对象在进入函数体前已经构造完成,也就是说在成员初始化列表处进行构造对象的工作,调用构造函数,在进入函数体之后,进行的是对已经构造好的类对象的赋值,又调用个拷贝赋值操作符才能完成(如果并未提供,则使用编译器提供的默认按成员赋值行为)

注意点:

初始化列表的成员初始化顺序:

C++ 初始化类成员时,是按照声明的顺序初始化的,而不是按照出现在初始化列表中的顺序。

class CMyClass {
    CMyClass(int x, int y);
    int m_x;
    int m_y;
};

CMyClass::CMyClass(int x, int y) : m_y(y), m_x(m_y)
{
};

你可能以为上面的代码将会首先做 m_y=I,然后做 m_x=m_y,最后它们有相同的值。但是编译器先初始化 m_x,然后是 m_y,,因为它们是按这样的顺序声明的。结果是 m_x 将有一个不可预测的值。有两种方法避免它,一个是总是按照你希望它们被初始化的顺序声明成员,第二个是,如果你决定使用初始化列表,总是按照它们声明的顺序罗列这些成员。这将有助于消除混淆。

原文地址:http://www.cnblogs.com/BlueTzar/articles/1223169.html

===================

在C++中,类的构造函数可以使用以下两种方式进行初始化:

构造函数的初始化列表(Initialization List)
构造函数的初始化列表可以在构造函数的参数列表后面使用冒号分隔符,然后在冒号后面列出每个成员变量及其对应的初始化值。例如:

class MyClass
{
public:
MyClass(int value) : m_value(value) {}

private:
int m_value;
};
1
2
3
4
5
6
7
8
在这个例子中,构造函数使用了初始化列表来初始化m_value成员变量。

在构造函数体内赋值(Assignment in Constructor Body)
构造函数也可以在其函数体内使用赋值语句来初始化成员变量。例如:

class MyClass
{
public:
MyClass(int value)
{
m_value = value;
}

private:
int m_value;
};
1
2
3
4
5
6
7
8
9
10
11
在这个例子中,构造函数使用了赋值语句来初始化m_value成员变量。

需要注意的是,使用初始化列表进行成员变量初始化比在构造函数体内使用赋值语句效率更高,并且可以用于初始化const成员变量或引用类型成员变量
————————————————

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。

原文链接:https://blog.csdn.net/Aheaboy/article/details/130513295

=================

 

参考:

https://blog.csdn.net/Aheaboy/article/details/130513295

标签:初始化,int,成员,c++,Base,public,构造函数
From: https://www.cnblogs.com/rebrobot/p/18220428

相关文章

  • QT C++实现表头添加组件
    QTableWidget在表头添加组件QTableWidget的表头有一个类QHeaderView是专门处理表头的类,要在表头添加一个类似QCheckBox的组件需要重写一个类并继承QHeaderView。QTableWidget中添加复选框并实现全选居中:自定义类MyHeaderView并继承QHeaderView自定义类MyTableWidget并继承QTa......
  • Java语言,MySQL数据库;SSM 心理咨询预约管理系统19086(免费领源码)计算机毕业设计项目推荐
    目 录摘要1绪论1.1背景及意义1.2研究现状1.3ssm框架介绍1.4论文结构与章节安排2 心理咨询预约管理系统系统分析2.1可行性分析2.1.1技术可行性分析2.1.2经济可行性分析2.1.3法律可行性分析2.2系统功能分析2.2.1功能性分析2.2.2非功能......
  • 从 .NET 到 C++:集合处理的思考
    1.IEnumerable与List的比较在.NET中,IEnumerable和List是两种常用的集合类型,它们有各自的适用场景:IEnumerable<T>:优点:惰性求值,仅在迭代时计算,适用于大数据集或流式数据处理。缺点:不能进行索引访问,没有直接的添加或移除元素的方法。List<T>:优点:支持索引访问,具备......
  • c++ 11 常用特性
     C++11常用特性总结 非原创,转载出处 http://www.cnblogs.com/feng-scC++11已经出来很久了,网上也早有很多优秀的C++11新特性的总结文章,在编写本博客之前,博主在工作和学习中学到的关于C++11方面的知识,也得益于很多其他网友的总结。本博客文章是在学习的基础上,加上博......
  • c++ 设计模板
     ========一、设计模式的分类总体来说设计模式分为三大类创建型模式,共五种:工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式。结构型模式,共七种:适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享元模式。行为型模式,共十一种:策略模式、模板......
  • c++ new delete
     =====================delete是我们c++中的关键字,我们都知道它和new是配套使用的,有new就有delete不然会存在内存泄漏的问题。但是我们使用过delete的同学可能会发现delete有两种形式,一种是delete[],那这两个有什么区别呢?1、delete释放new分配的单个对象指针指向的......
  • C++中的vector
    C++中的vector一丶vector的介绍和使用1.vector的简单介绍2.vector的使用I.构造函数II.相关的迭代器III.空间容量以及大小IV.vector的增删查改3.迭代器失效的问题二丶vector的深度剖析和模拟实现1.使用memcpy拷贝出现的问题2.vector的模拟实现一丶vector的介绍和......
  • C++ - 结构体转cha*
    c语言结构体转cha*在C语言中,将结构体转换为char*通常意味着你想要获取结构体的内存表示,并将其视为字符数组。这种转换可以通过使用memcpy函数来实现。下面是一个简单的例子,展示了如何将结构体转换为char*: #include<stdio.h>#include<stdlib.h>#include<string.......
  • C++ - tcp网络传输如何发送结构体类型
    1、tcp网络传输如何发送结构体类型 在C++中,要通过TCP网络传输结构体类型,你需要将结构体序列化为字节流,然后在另一端反序列化。这里有一个简单的例子:#include<iostream>#include<cstring>#include<sys/socket.h>#include<netinet/in.h>#include<unistd.h>//假设......
  • c++ string 使用例子
      ===============一文读懂C++String类在算法竞赛中的常见用法string相较于C语言的字符数组可方便太多了,在算法竞赛中能大大节省我们的时间。以下是我在刷题中会使用到的常见String用法。注释都写好了。#include<iostream>#include<string>usingnamespacestd;int......