1 构造函数和析构函数
1.1 构造函数和析构函数基础概念
构造函数语法:
构造函数函数名和类名相同,没有返回值,不能有void,但可以有参数。
Class Name(){}
析构函数语法:
析构函数函数名是在类名前面加”~”组成,没有返回值,不能有void,不能有参数,不能重载。
~ClassName(){}
1.2 构造函数的分类及调用
1.按参数类型:分为无参构造函数和有参构造函数
2.按类型分类:普通构造函数和拷贝构造函数(复制构造函数)
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <string>
using namespace std;
class Test
{
public :
Test ( )
{
cout << "构造函数!" << endl;
this - > a = 0 ;
this - > b = NULL ;
}
Test ( int a, const char * b)
{
cout << "默认有参构造!" << endl;
this - > a = a;
this - > b = ( char * ) malloc ( sizeof ( char ) * ( strlen ( b) + 1 ) ) ;
strcpy ( this - > b, b) ;
}
Test ( const Test & t1)
{
cout << "拷贝构造函数!" << endl;
this - > a = t1. a;
}
~ Test ( )
{
if ( this - > b != NULL )
{
free ( this - > b) ;
this - > b == NULL ;
}
cout << "析构函数!" << endl;
}
public :
int a;
char * b;
} ;
void test01 ( )
{
Test t1;
t1. a = 1000 ;
Test t2 ( 100 , "张三" ) ;
}
int main ( )
{
test01 ( ) ;
system ( "pause" ) ;
return 0 ;
}
1.3 拷贝构造函数详解
1.编译器也会提供默认的拷贝构造函数,进行成员变量的简单拷贝
2.拷贝构造函数的形参需要使用的是引用的方式
如果不是以引用的方式传入的话,就会导致一直死循环调用拷贝构造函数
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <string>
using namespace std;
class Test
{
public :
Test ( )
{
cout << "无参构造函数!" << endl;
this - > a = 0 ;
}
Test ( const Test & t1)
{
cout << "拷贝构造函数!" << endl;
this - > a = t1. a;
}
~ Test ( )
{
cout << "析构函数!" << endl;
}
public :
int a;
} ;
void test01 ( )
{
Test t2;
Test t3 ( t2) ;
}
class Maker3
{
public :
Maker3 ( int Ma)
{
cout << "有参构造函数" << endl;
ma = Ma;
}
Maker3 ( const Maker3& m)
{
cout << "拷贝构造函数" << endl;
}
private :
int ma;
} ;
void test02 ( )
{
Maker3 m1 ( 10 ) ;
Maker3 m2 ( m1) ;
Maker3 m3 = m1;
}
int main ( )
{
test01 ( ) ;
system ( "pause" ) ;
return 0 ;
}
1.4 匿名函数
匿名对象(显示调用构造函数),使用匿名对象初始化判断调用哪一个构造函数,
要看匿名对象的参数类型,并且匿名函数的生命周期是当前行,当前行结束后自动销毁。
注:不能调用拷贝构造函数去初始化匿名对象。
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <string>
using namespace std;
class Test
{
public :
Test ( )
{
cout << "无参构造函数!" << endl;
}
Test ( int a)
{
cout << "有参构造函数!" << endl;
}
Test ( const Test& t1)
{
cout << "拷贝构造函数!" << endl;
}
~ Test ( )
{
cout << "析构函数!" << endl;
}
} ;
void test01 ( )
{
Test ( ) ;
Test t1 = Test ( ) ;
cout << "test01()的销毁!" << endl;
}
int main ( )
{
test01 ( ) ;
system ( "pause" ) ;
return 0 ;
}
1.5拷贝构造函数的调用时机
1.对象以值传递的方式传给函数参数
2.用一个对象初始化另一个对象
3.函数局部对象以值传递的方式从函数返回(vs debug模式下调用一次拷贝构造,
qt不调用任何构造)
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <string>
using namespace std;
class Test
{
public :
Test ( )
{
cout << "无参构造函数!" << endl;
}
Test ( int a)
{
cout << "有参构造函数!" << endl;
}
Test ( const Test& t1)
{
cout << "拷贝构造函数!" << endl;
}
~ Test ( )
{
cout << "析构函数!" << endl;
}
} ;
void func1 ( Test t)
{
}
void test01 ( )
{
Test t1;
func1 ( t1) ;
}
void test02 ( )
{
Test t1;
Test t2 ( t1) ;
}
Test func2 ( )
{
Test t1;
cout << "被调函数局部对象t1的地址:" << & t1 << endl;
return t1;
}
void test03 ( )
{
Test t1 = func2 ( ) ;
cout << "主调函数局部对象t1的地址:" << & t1 << endl;
}
int main ( )
{
test03 ( ) ;
system ( "pause" ) ;
return 0 ;
}
1.6 构造函数调用规则
默认情况下,c++编译器至少为我们写的类增加3个函数:
1.默认构造函数(无参,函数体为空)
2.默认析构函数(无参,函数体为空)
3.默认拷贝构造函数,对类中非静态成员属性简单值拷贝
注:
1.如果用户定义拷贝构造函数,c++不会再提供任何默认构造函数
2.如果用户定义了普通构造(非拷贝),c++不在提供默认无参构造,但是会提供默认
拷贝构造。
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <string>
using namespace std;
class Test
{
public :
Test ( int a)
{
cout << "有参构造函数!" << endl;
}
~ Test ( )
{
cout << "析构函数!" << endl;
}
} ;
void test01 ( )
{
Test t1 ( 10 ) ;
Test t2 ( 1 ) ;
}
class Test1
{
public :
Test1 ( const Test1 & t)
{
cout << "拷贝构造函数!" << endl;
}
~ Test1 ( )
{
cout << "析构函数!" << endl;
}
} ;
void test02 ( )
{
}
int main ( )
{
test01 ( ) ;
system ( "pause" ) ;
return 0 ;
}
1.7 多个对象的构造和析构
1.初始化列表,是因为成员对象中的自定义数据类型的构造函数没有默认无参构造函
数,因为使用的是用户自定义的有参构造函数;初始化列表是调用成员对象的指定构造
函数。
2.如果有多个成员对象需要进行列表初始化,那么使用,隔开。
3.如果上面的构造函数是使用初始化列表的方式写的,那么之后的写的构造函数,
都要使用初始化列表的方式
4.如果类有成员对象是自定义数据类型,就先调用成员对象的构造函数,再调用自身
的,析构函数相反。
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <string>
using namespace std;
class Test1
{
public :
Test1 ( )
{
cout << "Test1()默认无参构造函数!" << endl;
}
~ Test1 ( )
{
cout << "Test1()析构函数!" << endl;
}
} ;
class Test2
{
public :
Test2 ( int a)
{
cout << "Test2()有参构造函数!" << endl;
}
~ Test2 ( )
{
cout << "Test2()析构函数!" << endl;
}
} ;
class Test3
{
public :
Test3 ( ) : t2 ( 10 )
{
cout << "Test3()默认无参构造函数!" << endl;
}
~ Test3 ( )
{
cout << "Test3()析构函数!" << endl;
}
private :
Test2 t2;
Test1 t1;
} ;
void test01 ( )
{
Test3 t;
}
void test02 ( )
{
Test3 t;
}
int main ( )
{
test02 ( ) ;
system ( "pause" ) ;
return 0 ;
}
1.8 深拷贝和浅拷贝
1、浅拷贝
同一类型的对象之间可以赋值,使得两个对象的成员变量的值相同,两个对象仍然是
独立的两个对象,这种情况被称为浅拷贝.
一般情况下,浅拷贝没有任何副作用,但是当类中有指针,并且指针指向动态分配的
内存空间,析构函数做了动态内存释放的处理,会导致内存问题。
2、深拷贝
当类中有指针,并且此指针有动态分配空间,析构函数做了释放处理,往往需要自定
义拷贝构造函数,自行给指针动态分配空间,深拷贝。
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <string>
using namespace std;
class Test1
{
public :
Test1 ( int a, int b)
{
this - > a = a;
this - > b = b;
cout << "Test1()默认无参构造函数!" << endl;
}
~ Test1 ( )
{
cout << "Test1()析构函数!" << endl;
}
public :
int a;
int b;
} ;
void test01 ( )
{
Test1 t1 ( 1 , 3 ) ;
Test1 t2 ( t1) ;
cout << "t1::a :" << t1. a << " t1::b :" << t1. b << endl;
cout << "t2::a :" << t2. a << " t2::b :" << t2. b << endl;
}
class Test2
{
public :
Test2 ( const char * a, int b)
{
this - > a = ( char * ) malloc ( strlen ( a) + 1 ) ;
strcpy ( this - > a, a) ;
this - > b = b;
cout << "Test1()默认无参构造函数!" << endl;
}
Test2 ( const Test2 & t)
{
this - > a = ( char * ) malloc ( strlen ( t. a) + 1 ) ;
strcpy ( this - > a, t. a) ;
this - > b = t. b;
}
~ Test2 ( )
{
if ( this - > a != NULL ) {
free ( this - > a) ;
this - > a = NULL ;
}
cout << "Test1()析构函数!" << endl;
}
public :
char * a;
int b;
} ;
void test02 ( )
{
Test2 t1 ( "张三" , 3 ) ;
Test2 t2 ( t1) ;
cout << "t1::a :" << t1. a << " t1::b :" << t1. b << endl;
cout << "t2::a :" << t2. a << " t2::b :" << t2. b << endl;
}
int main ( )
{
test02 ( ) ;
system ( "pause" ) ;
return 0 ;
}
2.explicit关键字
c++提供了关键字explicit,禁止通过构造函数进行的隐式转换。声明为explicit的
构造函数不能在隐式转换中使用。
[explicit注意]
1.explicit用于修饰构造函数,防止隐式转化。
2.是针对单参数的构造函数(或者除了第一个参数外其余参数都有默认值的多参构造)
而言。
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <string>
using namespace std;
class Test
{
public :
explicit Test ( int a)
{
cout << "有参构造函数调用" << endl;
}
} ;
int main ( )
{
system ( "pause" ) ;
return 0 ;
}
3.堆区开辟与释放
3.1 申请堆区空间和释放堆区空间
C++中解决动态内存分配的方案是把创建一个对象所需要的操作都结合在一个称为new
的运算符里。。当用new创建一个对象时,它就在堆里为对象分配内存并调用构造函数完
成初始化。new表达式的反面是delete表达式。delete表达式先调用析构函数,然后释
放内存。正如new表达式返回一个指向对象的指针一样,delete需要一个对象的地址。
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <string>
using namespace std;
class Test
{
public :
Test ( )
{
cout << "无参构造函数调用" << endl;
}
Test ( int a)
{
this - > a = a;
cout << "有参构造函数调用" << endl;
}
~ Test ( )
{
cout << "析构函数调用" << endl;
}
int a;
} ;
void test01 ( )
{
Test* t = ( Test* ) malloc ( sizeof ( Test) ) ;
free ( t) ;
}
void test02 ( )
{
Test* t = new Test;
delete ( t) ;
t = NULL ;
Test* t1 = new Test ( 10 ) ;
delete ( t1) ;
t1 = NULL ;
}
int main ( )
{
system ( "pause" ) ;
return 0 ;
}
3.2 用于数组的new和delete
当创建一个对象数组的时候,必须对数组中的每一个对象调用构造函数,除了在栈上
可以聚合初始化,必须提供一个默认的构造函数。
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <string>
using namespace std;
class Test
{
public :
Test ( )
{
cout << "无参构造函数调用" << endl;
}
Test ( int a)
{
this - > a = a;
cout << "有参构造函数调用" << endl;
}
~ Test ( )
{
cout << "析构函数调用" << endl;
}
int a;
} ;
void test03 ( )
{
int * p = new int [ 10 ] ;
for ( int i = 0 ; i < 10 ; i++ )
{
p[ i] = i;
}
for ( int i = 0 ; i < 10 ; i++ )
{
cout << p[ i] << endl;
}
delete [ ] p;
p = NULL ;
}
void test04 ( )
{
Test* p = new Test[ 10 ] ;
for ( int i = 0 ; i < 10 ; i++ )
{
p[ i] . a = i;
}
for ( int i = 0 ; i < 10 ; i++ )
{
cout << p[ i] . a << endl;
}
delete [ ] p;
p = NULL ;
}
int main ( )
{
system ( "pause" ) ;
return 0 ;
}
3.3 delete void* 可能会出错
如果对一个void*指针执行delete操作,这将可能成为一个程序错误,除非指针指向的
内容是非常简单的,因为它将不执行析构函数.以下代码未调用析构函数,导致可用内存
减少。
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <string>
using namespace std;
class Test
{
public:
Test ( )
{
cout << "无参构造函数调用" << endl;
}
Test ( int a)
{
this-> a = a;
cout << "有参构造函数调用" << endl;
}
~ Test ( )
{
cout << "析构函数调用" << endl;
}
int a;
} ;
void test05 ( )
{
void * p = new Test;
delete p;
}
int main ( )
{
test05 ( ) ;
system ( "pause" ) ;
return 0 ;
}
3.4 使用new和delete采用相同形式
即如果出现new就使用delete,如果出现malloc才使用free。
4. 静态成员
在类定义中,它的成员(包括成员变量和成员函数),这些成员可以用关键字static声
明为静态的,称为静态成员。不管这个类创建了多少个对象,静态成员只有一个拷贝,
这个拷贝被所有属于这个类的对象共享。
4.1.静态成员生命周期是整个程序结束之前,但是作业域仅在类内
4.2.静态成员变量要在类内声明,类外初始化
4.3.静态成员变量属于类不属于对象,是所有对象共享的
class Test
{
public :
Test ( ) {
cout << "无参构造函数" << endl;
}
public :
static int a;
} ;
int Test:: a = 10 ;
void test01 ( )
{
Test t1;
cout << t1. a << endl;
Test t2;
cout << t2. a << endl;
}
4.4.静态成员函数只能访问静态成员变量,不可以访问非静态成员变量
class Test1
{
public :
Test1 ( ) {
cout << "无参构造函数" << endl;
}
static void setA ( int a)
{
a1 = a;
cout << "a1 = " << a1 << endl;
}
public :
static int a1;
int b1;
} ;
int Test1:: a1 = 20 ;
void test02 ( )
{
Test1 t1;
t1. setA ( 50 ) ;
}
4.5.静态成员也是有访问权限的,如果不为公共的则类外不可以访问
class Test2
{
private :
static void setA ( )
{
cout << "a2 = " << a2 << endl;
}
private :
static int a2;
int b2;
} ;
int Test2:: a2 = 20 ;
void test03 ( )
{
Test2 t1;
}
4.6.const修饰的静态成员变量,最好在类内就进行初始化
class Test3
{
private :
const static int a3 = 10 ;
const static int b3;
} ;
const int Test3:: b3 = 20 ;
void test04 ( )
{
Test3 t1;
}
4.7.普通成员函数也是可以访问静态成员变量的
总结:普通成员函数可以访问静态成员变量,但是静态成员函数不可以访问静态成员变量.
5 C++面向对象模型
5.1 成员变量和函数的存储
在C++中,“数据”和“处理数据的操作(函数)”是分开存储的。
1.c++中的非静态数据成员直接内含在类对象中,就像c struct一样。
2.成员函数(member function)虽然内含在class声明之内,却不出现在对象中。
3.每一个非内联成员函数(non-inline member function)只会诞生一份函数实例.
5.2 类成员与类大小
1. 空类占的大小为1,因为编译器从内存上更好区分对象
class Test
{
} ;
void test01 ( )
{
cout << sizeof ( Test) << endl;
}
2.类的成员函数不占用类的大小;类的静态成员变量不占用类的大小;类的静态成员
函数不占用类的大小;类的非静态成员变量会占用类的大小。
class Test1
{
public :
void func ( )
{
}
static void func1 ( )
{
}
static int a;
int b;
} ;
int Test1:: a = 10 ;
void test02 ( )
{
cout << sizeof ( Test1) << endl;
}
int main ( )
{
test01 ( ) ;
test02 ( ) ;
system ( "pause" ) ;
return 0 ;
}
5.2 this指针
this指针工作原理:c++规定,this指针是隐含在对象成员函数内的一种指针。当一个
对象被创建后,它的每一个成员函数都含有一个系统自动生成的隐含指针this,成员函
数通过this指针即可知道操作的是那个对象的数据。用以保存这个对象的地址,也就是
说虽然我们没有写上this指针,编译器在编译的时候也是会加上的。因此this也称为“指
向本对象的指针”,this指针并不是对象的一部分,不会影响sizeof(对象)的结果。
5.2.1 当形参名和成员变量名相同时,用this指针区分
class Test
{
public :
Test ( int a, int b)
{
this - > a = a;
this - > b = b;
}
int a;
int b;
} ;
void test01 ( )
{
Test t1 ( 10 , 20 ) ;
t1. func ( ) ;
}
int main ( )
{
test01 ( ) ;
system ( "pause" ) ;
return 0 ;
}
5.2.2 返回对象的本身(在运算符重载的时候对本身进行连续的++的时候会用到)
class Test
{
public :
Test ( int a, int b)
{
this - > a = a;
this - > b = b;
}
void func ( )
{
cout << "a = " << this - > a << " " << "b = " << this - > b << endl;
}
Test& myTest ( )
{
return * this ;
}
int a;
int b;
} ;
void test01 ( )
{
Test t1 ( 10 , 20 ) ;
t1. func ( ) ;
}
int main ( )
{
test01 ( ) ;
system ( "pause" ) ;
return 0 ;
}
注:1.this指针指向的空间没有指向静态成员变量存储空间
2.this指针的指向是不允许修改的,因为本身是一个常量指针
3.实例化对象的作用:1.分配空间、 2.调用构造函数
class Test
{
public :
Test ( int a, int b)
{
this - > a = a;
this - > b = b;
}
void func ( )
{
cout << "a = " << this - > a << " " << "b = " << this - > b << endl;
}
Test& myTest ( )
{
return * this ;
}
static void func1 ( )
{
}
int a;
int b;
static int c;
} ;
int Test:: c = 10 ;
void test01 ( )
{
Test t1 ( 10 , 20 ) ;
t1. func ( ) ;
}
int main ( )
{
test01 ( ) ;
system ( "pause" ) ;
return 0 ;
}