C++中类成员初始化方式详细说明

在C++98中,支持了在类声明中使用等号“=”加初始值的方式,来初始化类中静态成员常量。这种声明方式我们也称之为“就地”声明。就地声明在代码编写时非常便利,不过C++98对类中就地声明的要求却非常高。如果静态成员不满足常量性,则不可以就地声明,而且即使常量的静态成员也只能是整型或者枚举型才能就地初始化。而非静态成员变量的初始化则必须在构造函数中进行。

1.初始化列表

class A
{
public:
    int a; // 初始化列表
    A(int a_):a(a_){}
};

2.构造函数初始化

class A
{
public:
    int a; // 初始化列表
    A(int a_, bool b) { a = a_; }
};

3.声明时初始化(也称就地初始化,c++11后支持)

class A
{
public:
    int a = 1; // 声明时初始化
    A() {}
};

在C++98中,支持了在类声明中使用等号“=”加初始值的方式,来初始化类中静态成员常量。这种声明方式我们也称之为“就地”声明。就地声明在代码编写时非常便利,不过C++98对类中就地声明的要求却非常高。如果静态成员不满足常量性,则不可以就地声明,而且即使常量的静态成员也只能是整型或者枚举型才能就地初始化。而非静态成员变量的初始化则必须在构造函数中进行。比如,如下代码在c++98中编译

class Init
{
public:
    Init(): a(0) []
    Init(int d): a(d) {}
private:
    int a;
    const static int b = 0;

    int c = 1;           // member, cannot pass build
    static int d = 0;    // member, cannot pass build

    static const double e = 1.3;      // not int or enum type, cannot pass build
    stati const char* const f = "e";  // not int or enum type, cannot pass build
}

这非常不方便,所以在C++11中,标准允许非静态成员变量的初始化有多种形式。具体而言,除了初始化列表外,在C++11中,标准还允许使用等号= 或者 花括号{} 进行就地的非静态成员变量初始化。

struct init {
    int a = 1;
    double b {1.2};
};

因此假如三种初始化方式同时存在的话,那么最后保留的成员变量值肯定是构造函数中初始化的值。

#include <iostream>
using namespace std;
class A 
{
public:
    int a = 1;
    A(int a_) :a(2) { a = 3; }
};

int main()
{
    A a;
    cout << "a.a=" << a.a << endl;
    return 0;
}

// a.a=3

4.如何选择初始化方式

4.1 声明时初始化的使用场景

一个优点是直观,你在声明的时候顺便给一个初始值,别人在看你代码的时候,点一下调到声明也能看到你赋予的初始值,不用再去看构造函数那里给的什么值
第二个优点更有用了,比如你要定义多个构造函数,每个构造函数都用列表初始化的方法初始化,很麻烦,我们来看一下下面这个例子:

class Group {
public:
    Group() {}
    Group(int a): data(a) {}
    Group(Mem m): mem(m) {}
    Group(int a, Mem m, string n): data(a), mem(m), name(n) {}
private:
    int data = 1;
    Mem mem{0};
    string name{"Group"};
};

4.2 列表初始化的使用场景

4.2.1 必须使用列表始化的四种情况
  1. 初始化一个reference成员
  2. 初始化一个const成员
  3. 调用一个基类的构造函数,而该函数有一组参数
  4. 调用一个数据成员对象的构造函数,而该函数有一组参数
4.2.2 那么成员初始化列表到底做了什么呢?
  • 编译器会一一操作初始化列表,以适当的顺序在构造函数之内安插初始化操作,并且在任何显示用户代码之前;
  • list中的项目顺序是由类中的成员声明顺序决定的,不是由初始化列表的顺序决定的;
4.2.3 为什么用成员初始化列表会快一些?
  • 下面的代码定义两个结构体,其中Test1有构造函数,拷贝构造函数及赋值运算符,为的是方便查看结果。Test2是个测试类,它以Test1的对象为成员,我们看一下Test2的构造函数是怎么样执行的。

代码1:

#include <iostream>
using namespace std;

struct Test1 {
	Test1() // 无参构造函数
	{
		cout << "Construct Test1" << endl;
	}

	Test1(const Test1& t1) // 拷贝构造函数
	{
		cout << "Copy constructor for Test1" << endl;
		this->a = t1.a;
	}

	Test1& operator = (const Test1& t1) // 赋值运算符
	{
		cout << "assignment for Test1" << endl;
		this->a = t1.a;
		return *this;
	}

	int a;
};

struct Test2 {
	Test1 test1;
	Test2(Test1& t1)
	{
		test1 = t1;
	}
};

int main()
{
	Test1 t1;
	Test2 t2(t1);
}
//Construct Test1
//Construct Test1
//assignment for Test1

解释一下,第一行输出对应调用代码中第一行,构造一个Test1对象。第二行输出对应Test2构造函数中的代码,用默认的构造函数初始化对象test1,这就是所谓的初始化阶段。第三行输出对应Test1的赋值运算符,对test1执行赋值操作,这就是所谓的计算阶段。
代码2:

#include <iostream>
using namespace std;

struct Test1 {
	Test1() // 无参构造函数
	{
		cout << "Construct Test1" << endl;
	}

	Test1(const Test1& t1) // 拷贝构造函数
	{
		cout << "Copy constructor for Test1" << endl;
		this->a = t1.a;
	}

	Test1& operator = (const Test1& t1) // 赋值运算符
	{
		cout << "assignment for Test1" << endl;
		this->a = t1.a;
		return *this;
	}

	int a;
};

struct Test2 {
	Test1 test1;
	Test2(Test1& t1) :test1(t1) 
	{
	
	}
};

int main()
{
	Test1 t1;
	Test2 t2(t1);
}

//Construct Test1
//Copy constructor for Test1

第一行输出对应 调用代码的第一行。第二行输出对应Test2的初始化列表,直接调用拷贝构造函数初始化test1,省去了调用默认构造函数的过程。所以一个好的原则是,能使用初始化列表的时候尽量使用初始化列表。

4.3 构造函数初始化的使用场景

4.3.1 派生类构造函数执行顺序

(1)虚拟基类的构造函数(多个虚拟基类则按照继承的顺序执行构造函数)。
(2)基类的构造函数(多个普通基类也按照继承的顺序执行构造函数)。
(3)类类型的成员对象的构造函数(按照初始化顺序)。
(4)派生类自己的构造函数。

4.3.2 构造函数初始化使用

(1)拷贝和赋值构造函数里(不然怎么叫赋值构造函数呢)
(2)这歌就是比较无聊的情况了,比如你想把几个成员函数都初始化成一个值,请看下面例子

class Group {
public:
    Group() {data1 = data2 = data3 = 0;}
private:
    int data1;
    int data2;
    int data3;
};

总而言之,优先就地初始化和列表初始化。

  • 4
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 在 C++ 中,可以在内部使用 static 关键字声明静态成员变量,并在外部初始化,例如: ```c++ class MyClass { public: static int myStaticVar; }; int MyClass::myStaticVar = 42; ``` 注意,静态成员变量必须在外部初始化。如果在内部初始化静态成员变量,编译器会报错。 ### 回答2: 在C语言中,可以通过在定义外部定义并初始化静态变量来在初始化静态变量。具体步骤如下: 1.在定义的头文件中声明静态变量。可以使用关键字"static"来修饰这个变量,表示它是一个静态成员。 2.在定义的源文件中定义并初始化静态变量。在源文件中,可以使用关键字"static"来修饰该变量,并将其初始化。 举例说明如下: // 定义头文件 MyClass.h #ifndef MYCLASS_H #define MYCLASS_H class MyClass{ private: static int staticVar; // 静态变量的声明 public: // 构造函数等其他成员函数的声明 }; #endif // 定义的源文件 MyClass.cpp #include "MyClass.h" int MyClass::staticVar = 0; // 静态变量的定义和初始化 // 在使用这个静态变量的其他函数中可以通过名加作用域解析符(::)来访问和修改静态变量的值 void foo(){ MyClass::staticVar = 10; // 修改静态变量的值 } 在这个例子中,通过在定义的源文件中定义并初始化静态变量"staticVar",可以实现在初始化静态变量。 ### 回答3: 在C语言中,我们无法直接在初始化静态变量。因为C语言中没有的概念,只有结构体。但是,可以使用以下方法来模拟在初始化静态变量的操作。 首先,在结构体中定义静态变量。然后,在函数中,使用静态变量之前,先判断该静态变量是否已经被初始化。如果没有被初始化,则在函数中初始化静态变量,并将其标记为已初始化。这样,即使多次调用该函数,静态变量也只会被初始化一次。 下面是一个示例代码: ```c #include <stdio.h> struct MyClass { static int staticVar; }; int initializeStaticVar() { static int isInitialized = 0; static int staticVar; if (!isInitialized) { // 在这里进行静态变量的初始化操作 staticVar = 10; isInitialized = 1; printf("静态变量已初始化\n"); } return staticVar; } int main() { printf("静态变量值:%d\n", initializeStaticVar()); printf("静态变量值:%d\n", initializeStaticVar()); return 0; } ``` 输出结果为: ``` 静态变量已初始化 静态变量值:10 静态变量值:10 ``` 通过这种方法,我们可以在C语言中模拟实现初始化静态变量的效果。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值