C++ 构造函数与析构函数,与成员初始化列表语法

题目:
所以此处不能调用参数为int的构造函数 。我认为此时编译器应该在所有带参构造函数中寻找适合的 。因为上述中的n可以被转换为short , 所以选择了参数是short的构造函数 。如果把参数short改为,编译器就会报错,因为n转换不为 。
1.2默认构造函数(无参数)的原型及定义
stock00.hclass Stock{private:std::string company;long shares;double share_cal;public:void acquire(const std::string & co,long n,double pr);void buy(long num,double price);void sell(long num,double price);//函数声明中,参数可以相同,毕竟是按照参数传递而不是按值传递//默认构造函数方式的原型Stock();}stock00.cpp..........//默认构造函数的定义Stock::Stock(){string company = "no name";shares = 0;share_cal = 0.0;}............
有些构造函数的原型中的参数也不一定非要是引用变量,下面这个可以作为参考
mytimee0.h........class Time{private:int hourse;int minutes;public:Time();Time(int h,int m = 0);void AddMin(int m);void AddHr(int h);void Reset(int h = 0,int m = 0);Time sum(const Time & t) const;void show() const;}
那么关于以上内容 , 在定义完对应类的对象时,我们应该如何使用呢?
显式调用构造函数(有等号)Stock food = Stock("World Cabbage",250,1.25);隐式调用构造函数(只有括号)Stock garmet("Furry Mason",50,2.5);当然了,默认构造函数和构造函数都写了那么就可以为所欲为的不进行调用Stock frist;Stock *pr = new Stock();这个时候编译器会自动调用默认构造函数的
使用对象数组时,如何进行调用 。
Stock my[4];//这就是对象数组my[0].show();//show为Stock类中的公共接口my[1].show();
//具体的调用过程const int STKS = 4;Stock stock[4] = {Stock("NanoSmart",12.5,20);Stock("Nanoscdasd",124.5,20);Stock( );};一共四个值 , 但是只调用了三个,前两个使用了构造函数,第三个使用了默认构造函数,第四个未出现的内容必然也是使用了默认构造函数 。
初始化对象数组的方案是,首先使用默认构造函数创建数组元素,然后花括号中的构造函数将创建临时对象,然后将临时对象的内容复制到相应的元素中 。
除了上述的定义默认构造函数的方法,还有一种方法
构造函数和默认构造函数二合一,一个函数原型一个函数定义
.........hStock(const std::string & co = “Error”,long n = 0,double pr = 0.0);//默认构造和构造函数一起//以上形式使用了默认参数.........cppStock::Stock(const string & co,long n,double pr){company = co;if(n<0){..............}..........share_val = pr;..........}
目前来说,就以上内容,显示隐式调用构造函数还是和以前一样 。调用默认构造函数就是就默认的值给相关的变量 。
1.3构造函数和&
一般情况下 , 构造函数都是 , 但是在单例模式下,比较特殊:
当我们想利用单例模式的时候,就可以把类的构造函数声明成的,这样就能保证外界不能实例多个对象出来了 。
下面看一个示例,关于构造函数的调用
题目1:
MyClass c1,*c2;MyClass *c3=new MyClass;MyClass &c4=c1;
c1要调用一次构造函数;
【C++ 构造函数与析构函数,与成员初始化列表语法】c2只是一个指针,用来动态描述对象,不会调用类的构造函数;
c3的右边新创建一个对象会调用构造函数 。但是注意,这里的赋值运算符不是类中的赋值运算符,而是普通的赋值运算符;
c4是一个对象引用,是c1的一个别名,所以不会调用构造函数 。
(题目和解答都来自牛客网,@)
只要类的对象被创建,就会执行构造函数
c1,创建对象c1,调用了构造函数;
c2,声明了一个指向类型的指针,未调用构造函数;
c3,new 在内存中创建了一个对象,并把对象地址赋给指针c3,创建对象调用了构造函数;
c4,将c4声明为引用,并将c1赋给它,即c4只是c1的一个引用 , 未调用构造函数 。
2关于析构函数(类的释放)
stock00.hclass Stock{private:std::string company;long shares;double share_cal;public:void acquire(const std::string & co,long n,double pr);void buy(long num,double price);void sell(long num,double price);//析构函数原型~Shock();}stock00.cpp//类声明#include#include"stock.h"//析构函数定义Stock::~Stock(){.....;//可以啥都没有//但是使用new分配空间时 , 记得使用delete释放//析构函数是和构造函数相对应的 , 当构造函数中使用new时,析构函数中需要使用delete进行删除}
在类定义的过程中声明函数的原型,在类声明中进行函数的定义
如果创建的是静态存储类对象,则其析构函数就在程序完成代码时自动被调用如何创建的是动态存储对象,则在其释放内存时,会被调用如何创建的是自动存储对象,则在完成其代码段之后,会被调用
总结:关于析构函数是和构造函数是对应的,当类定义的对象要被释放时,就会调用相应的析构函数
我们下面看一个例子:
设已经有A,B,C,D4个类的定义,程序中A,B,C,D析构函数调用顺序为?
C c;void main(){A*pa=new A();B b;static D d;delete pa;}
其中全局变量和静态局部变量时从 静态存储区中划分的空间,
二者的区别在于作用域的不同 , 全局变量作用域大于静态局部变量(只用于声明它的函数中),
而之所以是先释放 D 在释放 C的原因是,程序中首先调用的是 C的构造函数,然后调用的是 D 的构造函数,析构函数的调用与构造函数的调用顺序刚好相反 。
局部变量A 是通过 new 从系统的堆空间中分配的,程序运行结束之后,系统是不会自动回收分配给它的空间的 , 需要程序员手动调用来释放 。
局部变量 B 对象的空间来自于系统的栈空间,在该方法执行结束就会由系统自动通过调用析构方法将其空间释放 。
之所以是 先 A 后 B 是因为,B 是在函数执行到 结尾 "}" 的时候才调用析构函数,而语句a ; 位于函数结尾 "}" 之前
答案:
(来自牛客网:@老鼠)
以上题目很好的解释了,静态对象,动态对象和自动存储类对象析构函数的调用时机 。
示例:
stock00.h//类定义class Stock{private:std::string company;long shares;double share_cal;public:void buy(long num,double price);void sell(long num,double price);void update(double price);//默认构造函数的原型Stock();//构造函数的原型Stock(const std::string & co,long n = 0,double pr = 0.0);//析构函数的原型~Shock();}stock00.cpp//类声明#include#include"stock00.h"//默认构造函数的定义Stock::Stock(){string company = "no name";shares = 0;share_cal = 0.0;}//构造函数的定义Stock::Stock(const string & co,long n,double pr){company = co;if(n<0){..............}..........share_val = pr;..........}//析构函数的定义Stock::~Stock(){.....;//可以啥都没有//但是使用new分配空间时,记得使用delete释放//析构函数是和构造函数相对应的,当构造函数中使用new时,析构函数中需要使用delete进行删除}//类中对应的公共接口的类对象 , 书写相应的功能即可void Stock::buy(long num,double price){............ }void Stock::sell(long num,double price){.............}void Stock::update(double price){.............}usecjm.cpp//用户cpp#include#include"stock00.h"int main(){using std::name;Stock stock1("Nam",12,20.0);//隐式调用构造函数Stock stock2 = Stock ("Bono",2,2.0);//显式调用构造函数stock1.updata();stock2.updata();}
多个构造函数的情况(函数重载):
stonewt.h........class Stonewt{private:int stone;double pds_left;double pounds;public:Stonewt(double lbs);//构造函数1Stonewt(int stn,double lbs);//构造函数2Stonewt();//默认构造函数~Stonewt();//析构函数void show_lbs();void show_stn();};#endif
上述内容有三个构造函数(两个构造函数一个默认构造函数)
分别是让用户定义一个浮点或两个浮点数,也可以直接创造对象 , 而不进行初始化 。
Stonewt blossem(132.5);//创建对象使用构造函数1Stonewt buttercup(10,2);//创建对象使用构造函数2Stonewt bubbles;//创建对象使用默认构造函数
3new和对构造函数与析构函数的影响
注意事项:
如果在构造函数中使用new来初始化指针成员,则应该在析构函数中使用和必须相互兼容,new对应用,而new 【】对应于 【】;如果含有多个构造函数 , 则必须以相同的方式使用new,要么都带【】,要么都没有!因为只有一个析构函数,所有的构造函数中的new必须和析构函数中的匹配 。
构造函数
Person::Person():Age(0),name(""){qDebug()<<"进入构造函数";}Person::Person(int i){qDebug()<<"int i,QString C_name 进入构造函数";Age = i;}Person::Person(const int i,const QString C_name){qDebug()<<"int i,QString C_name 进入构造函数";Age = i;name = C_name;}Person::Person(const Person & p){qDebug()<<"const Person & p 进入构造函数";Age = p.Age;name = p.name;}Person::~Person(){qDebug()<<"进入析构函数";}
cpp
MainWindow::MainWindow(QWidget *parent) :QMainWindow(parent),ui(new Ui::MainWindow){ui->setupUi(this);//查隐式类型转换test01();test00();}void test00(){qDebug()<<"进入test00";Person * maxtrix = new Person[10];//堆区只能调用默认构造函数,所以使用new的时候务必确保有默认构造函数在Person max[3] = {Person(1),Person(1),Person(1)};//栈区可以调用其他类型的构造函数delete [] maxtrix;qDebug()<<"完成test00";}void test01(){qDebug()<<"进入test01";Person * new_p = new Person;delete new_p;qDebug()<<"完成test01";}
先看的输出
new为数组分配空间
类 * 变量名 = new 类[长度];
需要与之配套
[ ] 变量名;
在堆区,每次开辟一个空间 , 即会调用一次构造函数(且是默认构造函数),结束后即可释放 。如;
在栈区,可以选择其他带参数的构造函数 。如max[]
再看的输出结果
中是给正常指针分配空间并
4成员初始化列表(t)语法
常规构造函数:Quene::Quene(int qs){front = rear = NULL;item = 0;qsize = qs;}改进一下:Quene::Quene(int qs):qsize(qs),front(NULL),rear(NULL),item(0) {}
注意 , 只有构造函数可以使用这种初始化列表的方法 。
GraphModifier::GraphModifier(Q3DBars *bargraph): m_graph(bargraph),m_xRotation(0.0f),m_yRotation(0.0f),m_fontSize(30),m_segments(4),m_subSegments(3),m_minval(-20.0f),m_maxval(20.0f),//! [1]m_temperatureAxis(new QValue3DAxis),m_yearAxis(new QCategory3DAxis),m_monthAxis(new QCategory3DAxis),m_primarySeries(new QBar3DSeries),m_secondarySeries(new QBar3DSeries),//! [1]m_barMesh(QAbstract3DSeries::MeshBevelBar),m_smooth(false){//! [2]m_graph->setShadowQuality(QAbstract3DGraph::ShadowQualitySoftMedium);m_graph->activeTheme()->setBackgroundEnabled(false);m_graph->activeTheme()->setFont(QFont("Times New Roman", m_fontSize));m_graph->activeTheme()->setLabelBackgroundEnabled(true);m_graph->setMultiSeriesUniform(true);//! [2]m_months << "January" << "February" << "March" << "April" << "May" << "June" << "July" << "August" << "September" << "October" << "November" << "December";m_years << "2006" << "2007" << "2008" << "2009" << "2010" << "2011" << "2012" << "2013";
这是Qt中的一段程序 , 写的是构造函数 , 初值可以使用初始化列表进行,部分的内容是在构造函数内部进行初始化的 。
没事儿可以看看Qt的示例,类写的太漂亮了 。
5拷贝构造函数
用来举例的类:
class Person : public QMainWindow{Q_OBJECTpublic:Person();explicitPerson(const int i);Person(const int i,const QString C_name);Person(const Person & p);~Person();int Age;QString name;static int a;static void func();};
Person::Person():Age(0),name(""){qDebug()<<"进入构造函数";}Person::Person(int i){qDebug()<<"int i,QString C_name 进入构造函数";Age = i;}Person::Person(const int i,const QString C_name){qDebug()<<"int i,QString C_name 进入构造函数";Age = i;name = C_name;}Person::Person(const Person & p){qDebug()<<"进入拷贝构造函数";Age = p.Age;name = p.name;}Person::~Person(){qDebug()<<"进入析构函数";}
5.1拷贝构造函数形式