今天学习了一些初始化列表的相关知识

转自:http://blog.csdn.net/tianwailaibin/article/details/6322828

类成员变量的初始化不是按照初始化表的顺序被初始化的,而是按照在类中声明的顺序被初始化的。为什么会这样呢?我们知道,对一个对象的所有成员来说,它们的析构函数被调用的顺序总是和它们在构造函数里被创建的顺序相反。那么,如果允许上面的情况(即,成员按它们在初始化列表上出现的顺序被初始化)发生,编译器就要为每一个对象跟踪其成员初始化的顺序,以保证它们的析构函数以正确的顺序被调用。这会带来昂贵的开销。所以,为了避免这一开销,同一种类型的所有对象在创建(构造)和摧毁(析构)过程中对成员的处理顺序都是相同的,而不管成员在初始化列表中的顺序如何。

 

转自:http://hi.baidu.com/a708791/blog/item/d9f297af9fe3d8034b36d644.html

C++语言规定,在类的定义中,所有数据成员的说明语句都视为声明,而不是定义。所以,不能在类体中直接指定数据成员的初值,对象的初始化工作只能通过调用对象的构造函数来完成完成。在构造函数中,初始化列表扮演了十分重要的角色。对于普通的数据成员而言,使用初始化列表和在构造函数体内赋初值,效果是一样的。     但是,在另外一些情况下,只能使用初始化列表对成员进行初始化,否则会发生编译错误。

例如,数据成员是引用、常变量、类对象(该类没有提供不带参数的构造函数)等等

见下面的程序:
#include <iostream>
using namespace std;
class A
{
int num;
public:
A(int i){ num=i; }
void show(){ cout<<num<<endl; }
};
class B
{
int &r;
const double PI;
A a;
public:
B(int i);
void show()
{
   cout<<r<<endl;
   cout<<PI<<endl;
   a.show();
}
};
int e=5;
B::B(int i):r(e),PI(3.1415926),a(i){}
int main()
{
B(1).show();
return 0;
}
此程序能通过编译并正常运行。
     在类B的构造函数中,将初始化列表中的任何一个成员的初始化工作移到函数体内,都会导致编译错误。
B::B(int i){
r=e;                   //这是为引用赋值,并不是初始化
PI=3.1415926;   //常量的值不允许改变
a=A(i); //调用类B的构造函数之前,会先调用类A的构造函数A(),而此函数无定义
}
有时,程序员试图利用对象之间的赋值来代替初始化,而不是显式使用初始化列表。这样虽然不会发生编译错误,但却造成了程序运行效率的降低。
如下面的程序:
#include <iostream>
using namespace std;
class A
{
int num;
public:
A()
{
   cout<<"In default constructor"<<endl;
}
A(int i)
{
   cout<<"In user-defined constructor"<<endl;
   num=i;
}
A & operator=(const A& a)
{
   cout<<"Using assignment"<<endl;
   num=a.num;
   return *this;
}
void show()
{
   cout<<num<<endl;
}
};
class B
{
A a;
public:
B(int i);
void show()
{
   a.show();
}
};
B::B(int i)
{
a=A(i);
}
int main(int argc, char* argv[])
{
B b(1);
b.show();
return 0;
}
程序的输出结果是:
In default constructor
In user-defined constructor
Using assignment
1
   如果没有显式地在初始化列表中对成员对象a进行初始化,则在进入类B的构造函数体之前,会先调用类A的默认构造函数。在类B的构造函数体内进行的是赋值操作,并且要调用类A的构造函数A(int)先生成一个无名对象。这样的初始化方式不仅逻辑结构不清晰,而且效率低下。如果将类B的构造函数改写成:
B::B(int i):a(i){}
则程序的输出结果变成:
In user-defined constructor
1
这样不仅完成了初始化工作,而且效率高,避免了不必要的操作。
有些内容不管有没有显式写进初始化列表,都是会被“强行”加进去的。这些内容包括: ①如果该类是某个类的派生类,那么它的直接基类的构造函数一定要出现在初始化列表中。程序员如果没有显式地在初始化列表中调用直接基类的构造函数,编译器就会把直接基类的默认构造函数插入到初始化列表中。
②如果该类包含其他类的对象作为其数据成员,那么这些对象的初始化工作也一定要在初始化列表中完成。要么是程序员显式地在初始化列表中给出对象的构造形式,否则编译器会在初始化列表中调用这些对象的默认构造函数来完成初始化。
使用初始化列表还有一个问题要注意,初始化列表中各部分的执行顺序与它们的书写顺序无关,而是遵循这样的规则:基类的构造函数最先执行,其他的成员按照它们在类中声明的顺序依次被初始化。
考察下面的程序:
#include<iostream>
using namespace std;
class A
{
public:
int num;
A(int i)
{
   num=i;
   cout<<"In A constructor"<<endl;
}
};
class B
{
public:
int num;
B(int i)
{
   num=i;
   cout<<"In B constructor"<<endl;
}
};
class C
{
protected:
int num;
public:
C(int i)
{
   num=i;
   cout<<"In C constructor"<<endl;
}
};
class D:public C
{
A a;
int num;
B b;
public:
D(int i):num(i++),b(i++),a(i++),C(i++){}
void show()
{
   cout<<"C::num="<<C::num<<endl;
   cout<<"a.num="<<a.num<<endl;
   cout<<"num="<<num<<endl;
   cout<<"b.num="<<b.num<<endl;
}
};
void main()
{
D d=1;
d.show();
}
如果按照初始化列表的书写顺序执行,那么程序的输出结果应该是:
In B constructor
In A constructor
In C constructor
C::num=4
a.num=3
num=1
b.num=2
而实际上程序的运行结果是:
In C constructor
In A constructor
In B constructor
C::num=1
a.num=2
num=3
b.num=4
在初始化列表中,无法完成为对象的数组成员进行初始化的工作,原因是C++语言没有提供这样的机制,所以只能在构造函数体内分别为成员数组的每个元素分别赋初值。
如果数组元素本身也是对象,那么这种赋值操作会造成较大的运行时开销。问题还不止这些。如果在类体中定义一个常量数组,如何进行初始化呢?
见下面的程序:
#include<iostream>
int *CreateArr(int n)
{
int *p=new int[n];
for(int i=0;i<n;i++)
   p=i+1;
return p;
}
using namespace std;
class A
{
     int arrsize;
     const int * const arr;
public:
A(int n):arr(CreateArr(n)){ arrsize=n;}
void show()
{
   for(int i=0;i<arrsize;i++)
   cout<<arr<<' ';
   cout<<endl;
}
~A(){delete[] const_cast<int *>(arr);}
};
int main()
{
A a(3);
a.show();
return 0;
}
程序的输出结果是:
1 2 3    
     由于arr是一个指向常量的指针常量,所以,arr的初始化工作一旦完成,就不能改变arr数组元素的值,也不能将arr指向别处。客观上,arr与一个常量数组的作用是相同的。在上面的程序中,将函数CreateArr()移到类A体内作为该类的一个成员函数,程序运行结果是一样的。由于实际上是在构造函数中为arr分配了空间,所以应该在类A的析构函数函数中释放这部分空间,即在类A中定义:
~A(){delete[] arr;}
这样就能保证不发生内存泄漏。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
Go语言(也称为Golang)是由Google开发的一种静态强类型、编译型的编程语言。它旨在成为一门简单、高效、安全和并发的编程语言,特别适用于构建高性能的服务器和分布式系统。以下是Go语言的一些主要特点和优势: 简洁性:Go语言的语法简单直观,易于学习和使用。它避免了复杂的语法特性,如继承、重载等,转而采用组合和接口来实现代码的复用和扩展。 高性能:Go语言具有出色的性能,可以媲美C和C++。它使用静态类型系统和编译型语言的优势,能够生成高效的机器码。 并发性:Go语言内置了对并发的支持,通过轻量级的goroutine和channel机制,可以轻松实现并发编程。这使得Go语言在构建高性能的服务器和分布式系统时具有天然的优势。 安全性:Go语言具有强大的类型系统和内存管理机制,能够减少运行时错误和内存泄漏等问题。它还支持编译时检查,可以在编译阶段就发现潜在的问题。 标准库:Go语言的标准库非常丰富,包含了大量的实用功能和工具,如网络编程、文件操作、加密解密等。这使得开发者可以更加专注于业务逻辑的实现,而无需花费太多时间在底层功能的实现上。 跨平台:Go语言支持多种操作系统和平台,包括Windows、Linux、macOS等。它使用统一的构建系统(如Go Modules),可以轻松地跨平台编译和运行代码。 开源和社区支持:Go语言是开源的,具有庞大的社区支持和丰富的资源。开发者可以通过社区获取帮助、分享经验和学习资料。 总之,Go语言是一种简单、高效、安全、并发的编程语言,特别适用于构建高性能的服务器和分布式系统。如果你正在寻找一种易于学习和使用的编程语言,并且需要处理大量的并发请求和数据,那么Go语言可能是一个不错的选择。
Go语言(也称为Golang)是由Google开发的一种静态强类型、编译型的编程语言。它旨在成为一门简单、高效、安全和并发的编程语言,特别适用于构建高性能的服务器和分布式系统。以下是Go语言的一些主要特点和优势: 简洁性:Go语言的语法简单直观,易于学习和使用。它避免了复杂的语法特性,如继承、重载等,转而采用组合和接口来实现代码的复用和扩展。 高性能:Go语言具有出色的性能,可以媲美C和C++。它使用静态类型系统和编译型语言的优势,能够生成高效的机器码。 并发性:Go语言内置了对并发的支持,通过轻量级的goroutine和channel机制,可以轻松实现并发编程。这使得Go语言在构建高性能的服务器和分布式系统时具有天然的优势。 安全性:Go语言具有强大的类型系统和内存管理机制,能够减少运行时错误和内存泄漏等问题。它还支持编译时检查,可以在编译阶段就发现潜在的问题。 标准库:Go语言的标准库非常丰富,包含了大量的实用功能和工具,如网络编程、文件操作、加密解密等。这使得开发者可以更加专注于业务逻辑的实现,而无需花费太多时间在底层功能的实现上。 跨平台:Go语言支持多种操作系统和平台,包括Windows、Linux、macOS等。它使用统一的构建系统(如Go Modules),可以轻松地跨平台编译和运行代码。 开源和社区支持:Go语言是开源的,具有庞大的社区支持和丰富的资源。开发者可以通过社区获取帮助、分享经验和学习资料。 总之,Go语言是一种简单、高效、安全、并发的编程语言,特别适用于构建高性能的服务器和分布式系统。如果你正在寻找一种易于学习和使用的编程语言,并且需要处理大量的并发请求和数据,那么Go语言可能是一个不错的选择。
Go语言(也称为Golang)是由Google开发的一种静态强类型、编译型的编程语言。它旨在成为一门简单、高效、安全和并发的编程语言,特别适用于构建高性能的服务器和分布式系统。以下是Go语言的一些主要特点和优势: 简洁性:Go语言的语法简单直观,易于学习和使用。它避免了复杂的语法特性,如继承、重载等,转而采用组合和接口来实现代码的复用和扩展。 高性能:Go语言具有出色的性能,可以媲美C和C++。它使用静态类型系统和编译型语言的优势,能够生成高效的机器码。 并发性:Go语言内置了对并发的支持,通过轻量级的goroutine和channel机制,可以轻松实现并发编程。这使得Go语言在构建高性能的服务器和分布式系统时具有天然的优势。 安全性:Go语言具有强大的类型系统和内存管理机制,能够减少运行时错误和内存泄漏等问题。它还支持编译时检查,可以在编译阶段就发现潜在的问题。 标准库:Go语言的标准库非常丰富,包含了大量的实用功能和工具,如网络编程、文件操作、加密解密等。这使得开发者可以更加专注于业务逻辑的实现,而无需花费太多时间在底层功能的实现上。 跨平台:Go语言支持多种操作系统和平台,包括Windows、Linux、macOS等。它使用统一的构建系统(如Go Modules),可以轻松地跨平台编译和运行代码。 开源和社区支持:Go语言是开源的,具有庞大的社区支持和丰富的资源。开发者可以通过社区获取帮助、分享经验和学习资料。 总之,Go语言是一种简单、高效、安全、并发的编程语言,特别适用于构建高性能的服务器和分布式系统。如果你正在寻找一种易于学习和使用的编程语言,并且需要处理大量的并发请求和数据,那么Go语言可能是一个不错的选择。
Go语言(也称为Golang)是由Google开发的一种静态强类型、编译型的编程语言。它旨在成为一门简单、高效、安全和并发的编程语言,特别适用于构建高性能的服务器和分布式系统。以下是Go语言的一些主要特点和优势: 简洁性:Go语言的语法简单直观,易于学习和使用。它避免了复杂的语法特性,如继承、重载等,转而采用组合和接口来实现代码的复用和扩展。 高性能:Go语言具有出色的性能,可以媲美C和C++。它使用静态类型系统和编译型语言的优势,能够生成高效的机器码。 并发性:Go语言内置了对并发的支持,通过轻量级的goroutine和channel机制,可以轻松实现并发编程。这使得Go语言在构建高性能的服务器和分布式系统时具有天然的优势。 安全性:Go语言具有强大的类型系统和内存管理机制,能够减少运行时错误和内存泄漏等问题。它还支持编译时检查,可以在编译阶段就发现潜在的问题。 标准库:Go语言的标准库非常丰富,包含了大量的实用功能和工具,如网络编程、文件操作、加密解密等。这使得开发者可以更加专注于业务逻辑的实现,而无需花费太多时间在底层功能的实现上。 跨平台:Go语言支持多种操作系统和平台,包括Windows、Linux、macOS等。它使用统一的构建系统(如Go Modules),可以轻松地跨平台编译和运行代码。 开源和社区支持:Go语言是开源的,具有庞大的社区支持和丰富的资源。开发者可以通过社区获取帮助、分享经验和学习资料。 总之,Go语言是一种简单、高效、安全、并发的编程语言,特别适用于构建高性能的服务器和分布式系统。如果你正在寻找一种易于学习和使用的编程语言,并且需要处理大量的并发请求和数据,那么Go语言可能是一个不错的选择。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值