2 tianliangcsdn tianliangcsdn 于 2016.04.12 09:36 提问

c++下面两种构造函数的实现方式有什么区别?为什么?( 请说详细点 谢谢)
c++

c++下面两种构造函数的实现方式有什么区别?为什么?( 请说详细点 谢谢)
一:Savingaccount::Savingaccount(int date,int id,double rate):id(id),balance(0),rate(rate),lastdate(date),accumulation(0)
{ //构造函数的第一种实现方式

cout<<date<<"\t#"<<id<<"is created"<<endl;

}
二:Savingaccount::Savingaccount(int date,int id,double rate)
{ //构造函数的第二种实现方式
id=id;
balance=0;
rate=rate;
lastdate=date;
accumulation=0;
cout<<date<<"\t#"<<id<<"is created"<<endl;
}

5个回答

noahzuo
noahzuo   2016.04.12 09:52

前一种是使用初始化列表,后一种是在构造函数中进行赋值。

  1. 先看简单类成员的初始化:
 class Animal
{
public:
    Animal(int weight,int height):       //A初始化列表
      m_weight (weight),
      m_height (height)
    {
    }
    Animal(int weight,int height)       //B函数体内初始化
    {
        m_weight = weight;
        m_height = height;
    }
private:
    int m_weight ;
    int m_height ;
};

列表初始化和函数体内进行初始化都很简单,而且效率上也差不多(当然,上面只是为了说明问题而写的代码,上面两种方式是不能共存的)。

  1. 再看看继承关系中的构造函数:
 class Animal
{
public:
    Animal(int weight,int height):        //没有提供无参的构造函数
      m_weight (weight),
      m_height (height)
    {
}
private:
    int m_weight ;
    int m_height ;
};

class Dog: public Animal
{
public:
    Dog(int weight,int height,int type)   //error 构造函数 父类Animal无合适构造函数
    {
    }
private:
    int m_type ;
};

在这里可以看到,下面那种方法是错误的,这是因为在继承的时候,是先调用父类的构造函数,再调用子类的构造函数。但是发现父类没有默认的构造函数,因此会出错。

所以我们正确的做法是:

 class Dog: public Animal
{
public:
    Dog(int weight,int height,int type):
        Animal (weight, height)         //必须使用初始化列表增加对父类的初始化
    {
    }
private:
    int m_type ;
};

  1. const成员必须在初始化列表中初始化 很简单,const成员是不可以修改的,因此只可以在初始化列表中进行初始化。
 class Dog: public Animal
{
public:
    Dog(int weight,int height,int type):
        Animal (weight, height),
        LEGS (4)                 //必须在初始化列表中初始化
    {
        //LEGS = 4;           //error
    }
private:
    int m_type ;
    const int LEGS;
};

  1. 自定义成员在初始列表中进行初始化时将大大提升效率
class Food
{
public:
    Food(int type = 10)
    {
        m_type = 10 ;
    }
    Food(Food &other)                  //拷贝构造函数
    {
        m_type = other. m_type;
    }
    Food & operator =( Food &other )      // 重载赋值=函数
    {
        m_type = other. m_type;
        return * this;
    }
private:
    int m_type ;
};
  • 构造函数赋值方式 初始化成员对象m_food
class Dog: public Animal
{
public:
    Dog(Food &food)
    {
        m_food = food;                //初始化 成员对象
    }
private:
    Food m_food;
};

如果采用上面的方式,那么实际上会先执行对象类型构造函数 Food(int type = 10),然后执行对象类型构造函数 Food & operator =(Food &other)。

Think about why...

  • 构造函数初始化列表方式
 class Dog: public Animal
{
public:
    Dog(Food &food)
      :m_food (food)                   //初始化 成员对象
    {
        //m_food = food;              
    }
private:
    Food m_food;
};

在上面的代码中应该是直接调用拷贝构造函数完成初始化的……

noahzuo
noahzuo 回复tianliangcsdn: 你应该是新手吧,代码里有些地方还需要改进。最大的问题就是类型转换,你在double和int之间进行了太多操作,来来往往的隐式类型转换可能会带来问题。
2 年多之前 回复
noahzuo
noahzuo 回复tianliangcsdn: 你应该是新手吧,代码里有些地方还需要改进。最大的问题就是类型转换,你在double和int之间进行了太多操作,来来往往的隐式类型转换可能会带来问题。
2 年多之前 回复
tianliangcsdn
tianliangcsdn 完整代码如下,为什么当我用在构造函数中赋值的方式时是不对的呀?
2 年多之前 回复
tianliangcsdn
tianliangcsdn   2016.04.12 10:00

class Savingaccount //个人银行账户管理系统
{
private:
int id;
double balance;
double rate;
int lastdate;
double accumulation;
void record(int date,double amount);
double accumulate(int date)const{
return accumulation+balance*(date-lastdate);
}
public:
Savingaccount(int date,int id,double rate);
int getId(){return id;}
double getBalance(){return balance;}
double getRate(){return rate;}
void deposit(int date,double amount);
void withdraw(int date,double amount);
void settle(int date);
void show();
};
Savingaccount::Savingaccount(int date,int id,double rate)//:id(id),balance(0),rate(rate),lastdate(date),accumulation(0)
{ //构造函数的第二种实现方式
id=id;
balance=0;
rate=rate;
lastdate=date;
accumulation=0;
cout< }
void Savingaccount::record(int date,double amount){
accumulation=accumulate(date);
lastdate=date;
amount=floor(amount*100+0.5)/100;//保留小数点后两位数
balance+=amount;
cout }
void Savingaccount::deposit(int date,double amount){
record(date,amount);
}
void Savingaccount::withdraw(int date,double amount){
if(amount>getBalance())
cout<<"eror:not enough money "<<endl;
else
record(date,-amount);
}
void Savingaccount::settle(int date){
double interest=accumulate(date)*rate/365;
if(interest!=0)
record(date,interest);
accumulation=0;
}
void Savingaccount::show(){
cout<<"#"<<id<<"\tbalance:"<<balance;
}
int main(){
Savingaccount sa0(1,21325302,0.015);
Savingaccount sa1(1,58320212,0.015);
sa0.deposit(5,5000);
sa1.deposit(25,10000);
sa0.deposit(45,5500);
sa1.withdraw(60,4000);
sa0.settle(90);
sa1.settle(90);
sa0.show();cout<<endl;
sa1.show();cout<<endl;
system("pause");
return 0;
}

beifengche
beifengche   2016.04.12 10:08

第一种是使用初始化列表,第二种是在构造函数中进行赋值。前者发生的会更早一些,前者效率会高。类似于下面的代码:
假如我要给a一个初值10;
我可以写成
int a = 10;
也可以写成下面的方式。
into a;
a = 10;

qq_31597573
qq_31597573 回复tianliangcsdn: Savingaccount::Savingaccount(int m_date, int m_id, double m_rate),把参数的名字改改,不要和成员变量名一致,就可以了
2 年多之前 回复
tianliangcsdn
tianliangcsdn 上面评论里给出的完整代码用第二种方法为什么运行结果不对呀?
2 年多之前 回复
qq_31597573
qq_31597573   2016.04.12 10:19

第一种是用参数初始化列表进行初始化,第二种是函数体内赋值。针对引用,const类型的成员变量,以及没有默认构造函数的自定义数据类型必须用参数初始列表进行初始化。
比如:class School
{
public:
School(int number,int area,Student stu,int location):m_number(number),m_area(area),m_stu(stu)
{
m_location=location;
}
private:
const int m_number;
int& m_area;
Student m_stu;//Student类没有默认构造函数,但是有默认拷贝构造函数。
int m_location;
}
针对School类的前三个数据成员,必须用参数初始化列表进行初始化,如果在类里赋值会报错。而针对内置类型如m_location,参数初始化列表和函数体内赋值没有影响。
另外针对有默认构造函数的自定义成员变量。使用参数初始化列表进行初始化可以提高效率。
比如:
比如:class School
{
public:
School(Student stu):m_stu(stu)//只调用拷贝构造函数
{
// m_stu=stu;//调用构造函数+赋值操作
}
private:
Student m_stu;//Student类有默认构造函数,有默认拷贝构造函数。

}

可以看到参数初始化只执行一次操作,而函数体内赋值则需要两次,所以针对自定义数据类型(一般指类),用参数初始化列表更好。谢谢

sunnyloves
sunnyloves   2016.04.12 13:04

c++11的话直接声明之后就可以初始化了

 class Animal
{
public:
  .
private:
    int m_weight  = 1;
    int m_height  = 2;
};
Csdn user default icon
上传中...
上传图片
插入图片
准确详细的回答,更有利于被提问者采纳,从而获得C币。复制、灌水、广告等回答会被删除,是时候展现真正的技术了!
其他相关推荐
mysql, mysqladmin, mysqld之间的区别。
mysqld is the server executable (one of them)   #服务执行工具    mysql is the command line client  # 客户端工具   查询用mysqladmin is a maintainance or administrative utility  # 运维和管理工具...
线程有两种实现方法的具体区别
Java中有两种实现多线程的方式。一是直接继承Thread类,二是实现Runnable接口。那么这两种实现多线程的方式在应用上有什么区别呢?  第一种方式:使用Runnable接口创建线程 第二种方式:直接继承Thread类创建对象 使用Runnable接口创建线程 1.可以将CPU,代码和数据分开,形成清晰的模型 2.线程体run()方法所在的类可以从其它类中继承一些有
java实现线程的两种方法有什么区别?
如果一个类通过继承Thread来实现多线程的话,则不适合多个线程共享资源,而通过实现Runnable就可以做到这一点,下面给lz举个例子: Java code class MyTheard extends Thread{     private int num = 5;//不能声明为静态的全局变量     public void run(){         for(int i=0;i
c语言判断语句if(n==1)与if(1==n)的区别
下面是C语言中两种if语句判断方式。请问哪种写法更好?为什么? int n; if (n == 10) // 第一种判断方式 if (10 == n) // 第二种判断方式 首先,看看效率上有没有区别 if (i == 1) 004014CF cmp dword ptr [ebp-4],1
有两种实现多线程的方式以及两种方式之间的区别
Java中有两种实现多线程的方式。一是直接继承Thread类,二是实现Runnable接口。那么这两种实现多线程的方式在应用上有什么区别呢?          为了回答这个问题,我们可以通过编写一段代码来进行分析。我们用代码来模拟铁路售票系统,实现通过四个售票点发售某日某次列车的100张车票,一个售票点用一个线程表示。          我们首先这样编写这个程序: 
实现多线程的两种方式,区别和好处有哪些
一种是扩展java.lang.Thread类 另一种是实现java.lang.Runnable接口 区别就是:第一种是扩展,第二种是实现 好处就是: 在实际开发中通常以实现Runnable接口为主,因为实现Runnable接口相比继承Thread类可以避免继承的局限,一个类可以继承多个接口,适合于资源的共享。 以卖票程序为例,通过Thread类完成: package org.
两种跳转方式分别是什么?有什么区别
页面有两种方式: 1.forward跳转: 2.response跳转:response.sendRedirect("跳转页面地址"); forward跳转:\\服务器端跳转,地址栏不改变; response跳转:\\客户端跳转,地址栏改变; request.getRequestDispatcher("/message.jsp").forward(request, response);
Java中两种多线程实现方式的区别
在程序开发中用到多线程,正统的方法是使用Runnable接口,相比继承Thread类,Runnable接口有以下两点好处: 1、避免单继承机制的局限,一个类可以实现多个接口 2、适用于资源的共享 下面以买票程序为例,分析继承Thread类和实现Runnable接口的不同; 1、继承Thread类 package org.mole.xc; public class TicketThrea
两种跳转方式分别是什么?有什么区别?
在JSP中,跳转页面有两种方式:1.forward跳转:2.response跳转:response.sendRedirect("跳转页面地址"); 两种跳转的区别如下:1.forward跳转: a.服务器端跳转,地址栏不改变; b.执行到跳转语句后马上无条件跳转,之后的代码不再执行(跳转之前一定要释放全部资源); c.request设置的属性在跳转后的页面仍可以使用; d.使用传
我的Ubuntu配置
sudo passwd rootsudo update-alternatives --config editorsudo visudo sudo apt-get install vimsudo apt-get install gitsudo apt-get install build-essentialsudo vim /etc/default/grubsudo update-grub2 expo...