构造函数,导数中常见的构造函数
大家好,今天来为大家分享构造函数的一些知识点,和导数中常见的构造函数的问题解析,大家要是都明白,那么可以忽略,如果不太清楚的话可以看看本篇文章,相信很大概率可以解决您的问题,接下来我们就一起来看看吧!
什么是构造函数
构造函数是一种特殊的方法,主要用来在创建对象时初始化对象,即为对象成员变量赋初始值。
构造函数与其他方法的区别:
1、构造函数的命名必须和类名完全相同;而一般方法则不能和类名相同。
2、构造函数的功能主要用于在类的对象创建时定义初始化的状态。它没有返回值,也不能用void来修饰。这就保证了它不仅什么也不用自动返回,而且根本不能有任何选择。
3、构造函数不能被直接调用,必须通过new运算符在创建对象时才会自动调用,一般方法在程序执行到它的时候被调用。
4、当定义一个类的时候,通常情况下都会显示该类的构造函数,并在函数中指定初始化的工作也可省略,不过Java编译器会提供一个默认的构造函数。此默认构造函数是不带参数的。
5、当一个类只定义了私有的构造函数,将无法通过new关键字来创建其对象,当一个类没有定义任何构造函数,C#编译器会为其自动生成一个默认的无参的构造函数。
构造函数的例子如下:
class
{
public:
};
{
//函数体
}
如以下定义是合法的:
class T
{
public:
//构造函数允许直接写在类定义内,也允许有参数表。
private:int i;
};
当程序中没有析构函数时,系统会自动生成以下构造函数:
即不执行任何操作。
//注意若将本代码直接复制进编译器,可能会报错,原因是网页生成时会在代码前加一个中文占位符。
//最好将代码再写一次。
#include
using namespace std;
class time
{
public:
{
hour=0;
minute=0;
sec=0;
}
private:
int hour;
int minute;
int sec;
};
{
class time t1;
return 0;
}
c++什么叫构造函数,它的作用是什么
c++类的构造函数详解
一、构造函数是干什么的
class Counter
{
public:
//类Counter的构造函数
//特点:以类名作为函数名,无返回类型
Counter()
{
m_value= 0;
}
private:
//数据成员
int m_value;
}
该类对象被创建时,编译系统对象分配内存空间,并自动调用该构造函数->由构造函数完成成员的初始化工作
eg: Counter c1;
编译系统为对象c1的每个数据成员(m_value)分配内存空间,并调用构造函数Counter()自动地初始化对象c1的m_value值设置为0
故:
构造函数的作用:初始化对象的数据成员。
二、构造函数的种类
class Complex
{
private:
double m_real;
double m_imag;
public:
//无参数构造函数
//如果创建一个类你没有写任何构造函数,则系统会自动生成默认的无参构造函数,函数为空,什么都不做
//只要你写了一个下面的某一种构造函数,系统就不会再自动生成这样一个默认的构造函数,如果希望有一个这样的无参构造函数,则需要自己显示地写出来
Complex(void)
{
m_real= 0.0;
m_imag= 0.0;
}
//一般构造函数(也称重载构造函数)
//一般构造函数可以有各种参数形式,一个类可以有多个一般构造函数,前提是参数的个数或者类型不同(基于c++的重载函数原理)
//例如:你还可以写一个 Complex( int num)的构造函数出来
//创建对象时根据传入的参数不同调用不同的构造函数
Complex(double real, double imag)
{
m_real= real;
m_imag= imag;
}
//复制构造函数(也称为拷贝构造函数)
//复制构造函数参数为类对象本身的引用,用于根据一个已存在的对象复制出一个新的该类的对象,一般在函数中会将已存在对象的数据成员的值复制一份到新创建的对象中
//若没有显示的写复制构造函数,则系统会默认创建一个复制构造函数,但当类中有指针成员时,由系统默认创建该复制构造函数会存在风险,具体原因请查询有关“浅拷贝”、“深拷贝”的文章论述
Complex(const Complex& c)
{
//将对象c中的数据成员值复制过来
m_real= c.m_real;
m_img= c.m_img;
}
//类型转换构造函数,根据一个指定的类型的对象创建一个本类的对象
//例如:下面将根据一个double类型的对象创建了一个Complex对象
Complex::Complex(double r)
{
m_real= r;
m_imag= 0.0;
}
//等号运算符重载
//注意,这个类似复制构造函数,将=右边的本类对象的值复制给等号左边的对象,它不属于构造函数,等号左右两边的对象必须已经被创建
//若没有显示的写=运算符重载,则系统也会创建一个默认的=运算符重载,只做一些基本的拷贝工作
Complex&operator=( const Complex&rhs)
{
//首先检测等号右边的是否就是左边的对象本,若是本对象本身,则直接返回
if( this==&rhs)
{
return*this;
}
//复制等号右边的成员到左边的对象中
this->m_real= rhs.m_real;
this->m_imag= rhs.m_imag;
//把等号左边的对象再次传出
//目的是为了支持连等 eg: a=b=c系统首先运行 b=c
//然后运行 a=( b=c的返回值,这里应该是复制c值后的b对象)
return*this;
}
};
下面使用上面定义的类对象来说明各个构造函数的用法:
void main()
{
//调用了无参构造函数,数据成员初值被赋为0.0
Complex c1,c2;
//调用一般构造函数,数据成员初值被赋为指定值
Complex c3(1.0,2.5);
//也可以使用下面的形式
Complex c3= Complex(1.0,2.5);
//把c3的数据成员的值赋值给c1
//由于c1已经事先被创建,故此处不会调用任何构造函数
//只会调用=号运算符重载函数
c1= c3;
//调用类型转换构造函数
//系统首先调用类型转换构造函数,将5.2创建为一个本类的临时对象,然后调用等号运算符重载,将该临时对象赋值给c1
c2= 5.2;
//调用拷贝构造函数(有下面两种调用方式)
Complex c5(c2);
Complex c4= c2;//注意和=运算符重载区分,这里等号左边的对象不是事先已经创建,故需要调用拷贝构造函数,参数为c2
}
三、思考与测验
1.仔细观察复制构造函数
Complex(const Complex& c)
{
//将对象c中的数据成员值复制过来
m_real= c.m_real;
m_img= c.m_img;
}
为什么函数中可以直接访问对象c的私有成员?
2.挑战题,了解引用与传值的区别
Complex test1(const Complex& c)
{
return c;
}
Complex test2(const Complex c)
{
return c;
}
Complex test3()
{
static Complex c(1.0,5.0);
return c;
}
Complex& test4()
{
static Complex c(1.0,5.0);
return c;
}
void main()
{
Complex a,b;
//下面函数执行过程中各会调用几次构造函数,调用的是什么构造函数?
test1(a);
test2(a);
b= test3();
b= test4();
test2(1.2);
//下面这条语句会出错吗?
test1(1.2);//test1( Complex(1.2))呢?
}
四、附录(浅拷贝与深拷贝
上面提到,如果没有自定义复制构造函数,则系统会创建默认的复制构造函数,但系统创建的默认复制构造函数只会执行“浅拷贝”,即将被拷贝对象的数据成员的值一一赋值给新创建的对象,若该类的数据成员中有指针成员,则会使得新的对象的指针所指向的地址与被拷贝对象的指针所指向的地址相同,delete该指针时则会导致两次重复delete而出错。下面是示例:
【浅拷贝与深拷贝】
#include<iostream.h>
#include<string.h>
class Person
{
public:
//构造函数
Person(char* pN)
{
cout<<"一般构造函数被调用!\n";
m_pName= new char[strlen(pN)+ 1];
//在堆中开辟一个内存块存放pN所指的字符串
if(m_pName!= NULL)
{
//如果m_pName不是空指针,则把形参指针pN所指的字符串复制给它
strcpy(m_pName,pN);
}
}
//系统创建的默认复制构造函数,只做位模式拷贝
Person(Person& p)
{
//使两个字符串指针指向同一地址位置
m_pName= p.m_pName;
}
~Person()
{
delete m_pName;
}
private:
char* m_pName;
};
void main()
{
Person man("lujun");
Person woman(man);
//结果导致 man和 woman的指针都指向了同一个地址
//函数结束析构时
//同一个地址被delete两次
}
//下面自己设计复制构造函数,实现“深拷贝”,即不让指针指向同一地址,而是重新申请一块内存给新的对象的指针数据成员
Person(Person& chs);
{
//用运算符new为新对象的指针数据成员分配空间
m_pName=new char[strlen(p.m_pName)+ 1];
if(m_pName)
{
//复制内容
strcpy(m_pName,chs.m_pName);
}
//则新创建的对象的m_pName与原对象chs的m_pName不再指向同一地址了
}
构造函数的作用和用法
这个问题这么长时间没有合适的回答么?要搞清这个问题,首先要清楚构造函数的意义:构造函数并不是为了“初始化类”,至少不完全是。不然它就会改叫“Initializer”而不是“Constructor”。构造的内涵比初始化要丰富。尽管大多数情况下,我们对类的构造内容限于给它的属性赋值,但这仅仅是对简单的使用场景。对于复杂的设计模式,构造函数还要完成其他任务,从抽象的类去“建设”一个功能化的实例。比如依赖注入,最流行的模式就是使用构造函数。
假设你的类要使用一个服务,这个服务的作用是在类的方法工作时记录一些日志。但是日志的记录有可能会写在文本文件中、数据库中或直接打印在屏幕上。但是我们知道SOLID设计原则要求我们的类不应该知道这些细节,我们只要在需要调用日志功能的时候使用类似ILog.Write()之类的方法。那么这个ILog服务怎么“来到”我们的实例当中呢?假如你用属性赋值的方法,看上去可行,但是你会发现我们还是“知道”了这个ILog服务实例的存在,这破坏了SOLID原则,你在“使用服务”这外增加了“获取服务”这一不相关的任务,这时候就可以使用构造函数,在构造函数的参数中传入(ILog logService)这样一个实例,交给一个私有成员_logService,然后在类中使用_logService.Write()就好了。你可能会说,这不是换汤不换药吗?你把属性赋值变成了传参,不还是得写成ctor(logService);这样,logService哪来呢?这时候就要更进一步使用诸如Autofac之类的IOC类库,去完成依赖的注册。不过这些话题就有些远了。
总的来说,构造函数的意义在于“实例化一个具体功能的类”,这当然包括了“初始化”的部分,你可以查阅一些扩展资料,看看“初始化”之外的部分有多么神奇。
文章到此结束,如果本次分享的构造函数和导数中常见的构造函数的问题解决了您的问题,那么我们由衷的感到高兴!