C++学习笔记3
1.构造器
1.与类同名,无返回,在生成对象(栈、堆)时系统自动调用,一般用于初始化
2.可有参数,则可以默认参数、可以重载。重载和默认不能同时使用,但要包含无参构造器,即用默认参数实现:有参构造器包含了默认构造器,使其实现对象的无参创建。
3.若无构造器则系统默认生成一个无参构造器,若有构造器则不会生成无参构造器。 【无参构造器使用:Data s; 其中Data是构造器名,s为对象。有参构造器:Data s(1)】,此时无参和有参的构成重载。
2.析构器
1.与类同名,前面加~。无参无返回,对象消失时自动调用,用于对象销毁时的内存释放。若未提供,系统默认提供空析构器。
主函数:main.cpp
#include <iostream> #include"mystring.h" using namespace std; int main() { mystring s; mystring s2("哈哈"); cout<<s.c_str()<<endl; cout<<s2.c_str()<<endl; return 0; }
头文件:mystring.h
class mystring { public: mystring(const char *s=NULL); ~mystring(); char *c_str(); private: char *_str; };
文件:mystring.cpp
#include "mystring.h" #include<string.h> #include<iostream> using namespace std; mystring::mystring(const char * s) { if(s==NULL) { cout<<"调用无参构造器\n"; _str=new char[1]; *_str='\0'; } else { cout<<"调用有参构造器\n"; int len=strlen(s); _str=new char[len+1]; strcpy(_str,s); } } mystring::~mystring() { delete []_str; cout<<"调用析构器\n"; } char * mystring::c_str() { return _str; }
输出结果:
注意:
1.构造器和析构器一定要是public
2.其默认参数在声明中实现,定义中不能出现默认参数。
3.构造器和析构器实现时别忘在前面加其类名和作用域符号。
4.用于接收new分配的空间单位一定要是一个指针变量,赋初值时一定要是加上*(忘加的话析构的是你赋的初值作为地址的空间,我自己遇到的问题。)
5.用NULL时要包含头文件string.h或cstdio.h(在哪个文件中用就在哪个文件中加,否则会提示NULL不在其范围且编译不通过)
6.默认和重载都可以包含无参的情况,此代码是构造器利用默认参数,包含了无参的情况【此方法比较好】。
7.上面代码中,析构器先析构s2,再析构s。即先构造的后析构。
8.若定义mystring *p; 则要delete p; 其中析构器先析构的是p空间中的_str所指向的空间,再析构p。【其遵循的规律和6一样】
9.构造器不是为了创建对象,是为对象初始化【即分配空间】,析构器也不是用来释放对象的,而是在对象消失之前释放构造器为其分配的空间。
例如:
class A
{
public:
A()
{
name=new char[100];
}
char *name;
int age ;
};
int main()
{
A *a=new A ;
delete a;
return 0;
}
例子解析:其中主函数中new为*a分配了8个字节(char *+int),然后构造器为为*name分配了100个字节。所以在析构器中释放的是构造器中分配的空间,即释放100个字节。而主函数中的delete释放的是8个字节,要明白析构器是在对象消失之前调用,所以一切就说的通了,delete a时要先去执行析构器去释放,再去执行delete a。
参数列表:在函数声明之后,实现之前,用:开头,x(y)。用此方法对参数进行初始化,效率非常高。【目前不知道为什么,只知道其用法形式】
参数列表的初始化顺序与变量的声明顺序有关,与初始化表中的顺序无关。
class A
{
public:
mystring(int a,int b,int c)
:x(a),y(b),z(c)
{
}
private:
int x;
int y;
int z;
3.拷贝构造器:
调用时机:
1.由一个已存在的对象初始化新对象(不是赋值,是初始化),可理解为:将已存在的值拷贝一份来初始化新对象【String s("123"); String s2(s); 第二句为拷贝构造。】
2.传参数(参数是类的对象,指针),可理解为:将实参的值拷贝一份传给形参。参数是引用时,不会调用拷贝构造。
3.返回值是对象。【可理解为:首先要知道函数调用过程都是将函数压栈,在调用完之后要释放,则系统会将其返回值拷贝一份出来,这时则会调用拷贝构造器。其拷贝值存放地址不统一,有人将值拷贝到寄存器中,有的则是预编译时知道函数有返回值则在调用函数之前先在其栈上分配好一个空间用于存储拷贝值。】栈上的对象是可以返回的,但不能返回栈上的引用(除非返回的是对象本身)【】
1.形式:类名(const 类名 & 形参)【切记参数列表中传的是引用,不是指针。】
2.系统默认提供,一旦自定义,则系统不再提供。
3.系统提供的默认属于等位拷贝,即浅浅的拷贝。
4.浅拷贝会导致内存重析构,即同一空间释放两次。但有些情况(如有堆空间),要自实现拷贝构造。【即:当有要存放在堆空间的数据时要用自拷贝,但只是堆空间上的数据用其自实现方法实现,其他的依然用等位拷贝】
5.系统默认拷贝构造器是通过拷贝其对象里的内容(对象包含的地址),使其指向同一个地址达到其相同内容。自实现拷贝构造是通过新建一个与对象内所指向地址的相同存储空间,然后将其内容拷贝到该新建内存,使其达到相同内容。
【自拷贝中要用到的拷贝函数memcpy(),最后一个参数是拷贝数据的大小】
space =new char[another.size()];
memcpy(space,another,space,another._str);
【例子:【自实现】
mystring::mystring(const mystring &another)
{
int len = strlen(another.size());
_str =new char[len+1];
strcpy(_str,another._str);
}
】
6.字符串之间的赋值,其实=本质就是一个拷贝构造,用了赋值运算符重载
4.this指针:系统创建对象时,默认生成的一个指向当前对象的指针
作用:
1.避免构造器的入参与成员名相同
2.基于this指针的自身引用还被广泛的应用于那些支持多重串联调用的函数中(如:连续赋值)