1.转换构造函数
-
单个参数的构造函数,称之为转换构造函数
-
将其它类型转换为类类型
-
类的构造函数只有一个参数是非常危险的,因为编译器可以使用这种构造函数把参数的类型隐式转换为类类型
-
构造函数与析构函数的简单总结
(1)构造函数是一种特殊的成员函数
在创建对象的时候自动调用,对对象的数据成员进行初始化
(2)
栈区中创建对象,会在生存期结束的时候,自动调用析构函数(也叫析构造函数)
在堆上创建的对象(比如:new),要有程序员显式调用delete释放该对象,同时调用析构造函数
全局对象的构造先于main函数,全局对象生存期结束时,也会自动调用析构函数 -
eg:12cpp\12cpp\12cpp\01.cpp
#include "Clock.h"
int main(void)
{
Clock c(10,10,10);
c.Display();
//c.second_ += 1;
c.Update();
c.Display();
//c.hour_ = 11;
c.SetHour(11);
c.Display();
return 0;
}
- 类的定义以及初始化如下:
12cpp\12cpp\12cpp\Clock.h
//#pragma once
#ifndef _CLOCK_H_
#define _CLOCK_H_
class Clock
{
public:
Clock(int hour=0, int minute=0, int second=0);
~Clock();
void Display();
void Update();
int GetHour();
int GetMinute();
int GetSecond();
void SetHour(int hour);
void SetMinute(int minute);
void SetSecond(int second);
private:
int hour_;
int minute_;
int second_;
};
#endif // _CLOCK_H_
12cpp\12cpp\12cpp\Clock.cpp
#include "Clock.h"
#include <iostream>
using namespace std;
void Clock::Display()
{
cout<<hour_<<":"<<minute_<<":"<<second_<<endl;
}
Clock::Clock(int hour/* =0 */, int minute/* =0 */, int second/* =0 */)
{
hour_ = hour;
minute_ = minute;
second_ = second;
cout<<"Clock::Clock"<<endl;
}
Clock::~Clock()
{
cout<<"Clock::~Clock"<<endl;
}
void Clock::Update()
{
second_++;
if (second_ == 60)
{
minute_++;
second_ = 0;
}
if (minute_ == 60)
{
hour_++;
minute_ = 0;
}
if (hour_ == 24)
{
hour_ = 0;
}
}
int Clock::GetHour()
{
return hour_;
}
int Clock::GetMinute()
{
return minute_;
}
int Clock::GetSecond()
{
return second_;
}
void Clock::SetHour(int hour)
{
hour_ = hour;
}
void Clock::SetMinute(int minute)
{
minute_ = minute;
}
void Clock::SetSecond(int second)
{
second_ = second;
}
-
测试:
-
构造函数的作用:
(1)初始化
(2)类型转化(使用转换构造函数)
将其他类型转换为类类型 -
带一个参数的构造函数,可能是
(1)普通构造函数,作用:初始化
(2)转换构造函数,作用:初始化,类型转化 -
eg:12cpp\12cpp\12cpp\02.cpp
#include "Test.h"
int main(void)
{
//然这里也是调用一个参数的构造函数
//但是这里并不是充当转换的功能
Test t(10); // 带一个参数的构造函数,充当的是普通构造函数的功能
//隐式转换为类类型
//将一个整数转化为对象
t = 20; // 将20这个整数赋值给t对象
// 1、调用转换构造函数将20这个整数转换成类类型 (此时会生成一个临时对象)
// 2、将临时对象赋值给t对象(实际上是,调用的是=运算符(等号运算符,若没有提供,则调用默认的=号运算符))
//没有提供,则调用默认的=号运算符,类似:若没有提供一个构造函数,系统将提供一个默认的构造函数
//若没有提供一个析构函数,系统将提供一个默认的析构函数
Test t2;
return 0;
}
- 类声明及定义
12cpp\12cpp\12cpp\Test.h
#ifndef _TEST_H_
#define _TEST_H_
class Test
{
public:
// 如果类不提供任何一个构造函数,系统将为我们提供一个不带参数的
// 默认的构造函数
Test();
/*explicit */Test(int num);
void Display();
//将operator=看成一个函数,这是一个函数名
Test& operator=(const Test& other);
~Test();
private:
int num_;
};
#endif // _TEST_H_
12cpp\12cpp\12cpp\Test.h
#include "Test.h"
#include <iostream>
using namespace std;
// 不带参数的构造函数称为默认构造函数
Test::Test()
{
num_ = 0;
cout<<"Initializing Default"<<endl;
}
Test::Test(int num)
{
num_ = num;
cout<<"Initializing "<<num_<<endl;
}
Test::~Test()
{
cout<<"Destroy "<<num_<<endl;
}
void Test::Display()
{
cout<<"num="<<num_<<endl;
}
Test& Test::operator=(const Test& other)
{
cout<<"Test::operator="<<endl;
//可以防止t=t,同一个对象防止赋值
if (this == &other)
return *this;
//不同的对象。内容相同,也不做赋值,则需要用if (*this == other),
//但是前提是==号运算符需要重载
//将other对象中每一个成员,赋值给当前对象的每一个成员,逐成员赋值
num_ = other.num_;
return *this;//返回对象自身
}
- 测试:
普通构造函数,初始值为10;
t=20,会调用转换构造函数(只带一个参数),会构造一个临时对象出来,接着将临时对象的值通过=赋值给t,一旦赋值成功,临时对象就没有存在价值了,所以紧接着释放了临时对象,显示:Destroy 20;
临时对象已经释放了,最终会释放对象t(被赋值以后变成了20)和t2
2.赋值与初始化区别
- 在初始化语句中的等号不是运算符。编译器对这种表示方法有特殊的解释
- 赋值,赋值会调用等号=运算符,若没有,系统则提供一个默认的等号运算符
- 等号运算符:
Test& Test::operator=(const Test& other);
- eg:12cpp\12cpp\12cpp\03.cpp
#include "Test.h"
int main(void)
{
//Test t(10); // 带一个参数的构造函数,充当的是普通构造函数的功能
//t = 20; // 将20这个整数赋值给t对象
1、调用转换构造函数将20这个整数转换成类类型 (生成一个临时对象)
2、将临时对象赋值给t对象(调用的是=运算符)
//Test t2;
//在初始化语句中的等号不是运算符。编译器对这种表示方法有特殊的解释
//这里不是产生一个临时对象,将临时对象赋值给t,这里不是一个赋值操作,而是初始化操作
Test t = 10; // VS中,等价于Test t(10); 这里的=不是等号运算符(不是赋值操作),而是表示初始化。(若是Linux,10还是得先构造一个匿名对象)
//这里的20并不是对象,所以先将20转换构造一个临时对象出来,然后再将临时对象赋值给t,调用operate=
//接着销毁临时对象
t = 20; // 赋值操作,调用=号运算符(operate=)
//t2调用默认构造函数
Test t2;
//接着把t2赋值给t,调用=号运算符(operate=)
t = t2; // 赋值操作,等价于:t.operator=(t2);将operator=看成是一个函数,然后将t2参数
//传递进去,将t2的数据成员逐成员赋给t,最后返回的是t对象本身,因为t这里隐含的
//第一个参数是this指针,this指针指向自身t
return 0;
}
-
类声明及定义如上
-
测试:
3.explicit
-
只提供给类的构造函数使用的关键字。
-
编译器不会把声明为explicit的构造函数用于隐式转换,它只能在程序代码中显示创建对象
-
eg:12cpp\12cpp\12cpp\04cpp\03.cpp
#include "Test.h"
int main(void)
{
//不通过,原因是:阻止隐式转化,初始化语句就不等价于Test t(10)
Test t = 10;
//不通过
Test t;
t = 10;//不会通过的原因是:此时要转换一个临时对象出来赋值给t
return 0;
}
- 类声明及定义
12cpp\12cpp\12cpp\04cpp\Test.h
#ifndef _TEST_H_
#define _TEST_H_
class Test
{
public:
// 如果类不提供任何一个构造函数,系统将为我们提供一个不带参数的
// 默认的构造函数
Test();
explicit Test(int num);//表示只能显式构造对象,不能隐式构造对象
void Display();
//将operator=看成一个函数,这是一个函数名
Test& operator=(const Test& other);
~Test();
private:
int num_;
};
#endif // _TEST_H_
12cpp\12cpp\12cpp\04cpp\Test.cpp
#include "Test.h"
#include <iostream>
using namespace std;
// 不带参数的构造函数称为默认构造函数
Test::Test()
{
num_ = 0;
cout<<"Initializing Default"<<endl;
}
Test::Test(int num)
{
num_ = num;
cout<<"Initializing "<<num_<<endl;
}
Test::~Test()
{
cout<<"Destroy "<<num_<<endl;
}
void Test::Display()
{
cout<<"num="<<num_<<endl;
}
Test& Test::operator=(const Test& other)
{
cout<<"Test::operator="<<endl;
//可以防止t=t,同一个对象防止赋值
if (this == &other)
return *this;
//不同的对象。内容相同,也不做赋值,则需要用if (*this == other),
//但是前提是==号运算符需要重载
//将other对象中每一个成员,赋值给当前对象的每一个成员,逐成员赋值
num_ = other.num_;
return *this;//返回对象自身
}
- 测试