1 i++与++i的效率比较
在内建数据类型的情况下,效率没有区别
在自定义数据类型的情况下,++i 效率较高
2 类成员的初始化:
初始化列表的初始化顺序与变量声明的顺序一致,而不是按照出现在初始化列表中的顺序。
#include <iostream>
using namespace std;
class Obj
{
public:
Obj( int k ):j(k),i(j)
{
}
void print(void)
{
cout << "i = " << i << endl << "j = " << j << endl ;
}
private:
int i;
int j;
};
int main(void)
{
Obj obj(2);
obj.print();
return 0;
}
//输出结果是:
i = -858993460
j = 2
静态数据成员是类的成员,它为类所有的对象所共享。
静态成员函数与静态成员变量一样,不属于类的对象,因此不含有this指针,也就无法调用类的非静态成员。
静态数据成员不受private控制符作用。
静态数据成可以直接使用类名调用。
4 构造函数
构造函数的作用:对类的数据成员进行初始化和分配内存。
全局对象的构造函数在main函数之前执行。
构造函数可以被重载,因为构造函数可以有多个,而且可以带参数。
析构函数不可以被重载,因为析构函数只能有一个,并且不能带参数。
#include <iostream>
using namespace std;
struct CLS
{
int m_i;
CLS( int i):m_i(i)
{
cout << "CLS(): this = " << this << endl;
}
CLS()
{
CLS(0);
cout << "CLS(int): this = " << this << endl;
}
};
int main()
{
CLS obj;
cout << "&obj = " << &obj << endl;
cout << "obj.m_i = " << obj.m_i << endl;
return 0;
}
//输出结果是:
CLS(): this = 0027FD78
CLS(int): this = 0027FF24
&obj = 0027FF24
obj.m_i = 858993460
在带参数的构造函数里打印出来的对象地址和对象obj的地址不一致。实际上在CLS()中的调用CLS(0)只是栈上生成一个临时对象,对于自己毫无影响。
因此,构造函数的相互调用引起的后果不是死循环,而是栈溢出。
构造函数内调用构造函数只是在栈上生成了一个临时对象,对于其毫无影响。
普通构造函数能够被隐式调用,而explicit构造函数只能被显示调用。
5 析构函数
析构函数的作用:释放类的构造函数在整个生命周期中获得的资源
只有一个类被用来作为基类的时候,才会把析构函数写成虚函数。
析构函数的执行顺序与构造函数相反。
#include <iostream>
using namespace std;
class A
{
private:
int a;
public:
A(int aa)
{
a = aa;
cout << "Constructor A " << a << endl;
};
~A()
{
cout << "Destructor A " << a << endl;
}
};
class B:public A
{
private:
int b;
public:
B( int aa = 0, int bb =0):A(aa)
{
b = bb;
cout << "Constructor B " << b << endl;
}
~B()
{
cout << "Destructor B " << b << endl;
}
};
void main()
{
B obj1(5);
B obj2(6, 7);
//system("pause");
return;
}
//输出结果是:
Constructor A 5
Constructor B 0
Constructor A 6
Constructor B 7
Destructor B 7
Destructor A 6
Destructor B 0
Destructor A 5
6 复制构造函数
作用:特殊的构造函数,它由编译器调用来完成一些基于同一类的其他对象的构造以及初始化。
在C++中,3种对象需要复制,此时复制构造函数将会被调用
(1)一个对象以值传递的方式传入函数体
(2)一个对象以值传递的方式从函数返回
(3)一个对象需要通过另外一个对象进行初始化
浅复制是让新旧两个对象指向同一个外部的内容,而深复制是指新对象制作了外部对象的独立复制。
如果用户没有自定义复制构造函数,并且在代码中使用到了复制构造函数,编译器就会生成默认的复制构造函数;但是如果用户定义了复制构造函数,编译器就不会再生成复制构造函数。
如果用户定义了一个构造函数,但不是复制构造函数,而此时在代码中又使用到了复制构造函数,编译器也还会生成默认复制构造函数。
编写继承类的复制函数有一个原则:使用基类的复制构造函数。
7 赋值函数
构造函数与赋值函数的区别:
(1)复制构造是一个对象初始化一块内存区域,这块内存区域就是新对象的内存区;例如:
class A;
A a;
A b = a; //复制构造函数调用
A b(a); //复制构造函数调用
而赋值函数是对于一个已经被初始化的对象进行赋值的操作。例如:
class A;
A a;
A b;
b = a; //赋值函数调用
(2)一般涞说在数据成员包含指针对象的时候,应考虑两种不同的处理需求:一种是复制指针对象,另一种是引用指针对象。复制构造函数大多数情况下是复制,赋值函数则是引用对象。
(3)实现不一样。复制构造函数首先是一个构造函数,它调用的时候是通过参数对象的初始化产生一个对象。赋值函数则是把一个新对象赋值给一个原有的对象,所以如果原来的对象中有内存分配要先把内存释放掉,而且还要检查一下两个对象是不是同一个对象,如果是,则不做任何操作。
#include <iostream>
using namespace std;
class A
{
private:
int num;
public:
A()
{
cout << "Default constructor." << endl;
}
~A()
{
cout << "Destructor. " ;
cout << num << endl;
}
A(const A &a)
{
cout << "Copy constructor." << endl;
}
void operator= (const A& a)
{
cout << "Overload operator." << endl;
}
void SetNum(int n)
{
num = n;
}
};
void main()
{
A a1;
A a2(a1);
A a3 = a1;
A &a4 = a1;
a1.SetNum(1);
a2.SetNum(2);
a3.SetNum(3);
a4.SetNum(4);
}
//(当main函数退出时,对象析构顺序与调用构造函数顺序相反;a4为a1的一个引用,不调用构造函数或者赋值函数)
//输出结果是:
Default constructor.
Copy constructor.
Copy constructor.
Destructor 3
Destructor 2
Destructor 4
#include <iostream>
using namespace std;
class B
{
private:
<span style="white-space:pre"> </span>int data;
public:
<span style="white-space:pre"> </span>B()
<span style="white-space:pre"> </span>{
<span style="white-space:pre"> </span>cout << "Default constructor " << endl;
<span style="white-space:pre"> </span>}
<span style="white-space:pre"> </span>B(int i):data(i)
<span style="white-space:pre"> </span>{
<span style="white-space:pre"> </span> cout << "Constructor by parameter " << data << endl;
<span style="white-space:pre"> </span>}
//#注释部分#
// <span style="white-space:pre"> </span>B(B& b)
// <span style="white-space:pre"> </span>{
// <span style="white-space:pre"> </span> cout << "Copy constructor." << endl;
// <span style="white-space:pre"> </span>data = b.data;
// }
~B()
<span style="white-space:pre"> </span>{
<span style="white-space:pre"> </span>cout << "Destructor " << endl; ;
<span style="white-space:pre"> </span>}
};
B play(B b)
{
<span style="white-space:pre"> </span>return b;
}
int main(void)
{
B t1 = play(5);
B t2 = play(t1);
return 0;
}
// 输出结果1:
// Constructor by parameter 5 (调用带参数的构造函数在fun内生成的临时对象)
// Destructor (5传入fun时生成的临时对象析构)
// Destructor (t1传入fun时产生的返回的临时对象析构)
// Destructor (t2析构)
// Destructor (t1析构)
// 输出结果2: (去除#注释部分#)
// Constructor by parameter 5 (调用带参数的构造函数在fun内生成的临时对象)
// Copy constructor (调用复制构造函数把临时对象复制到t1)
// Destructor (fun内的临时对象析构)
// Copy constructor (调用复制构造函数在fun内产生临时对象)
// Copy constructor (调用复制构造函数把临时对象复制到t2)
// Destructor (fun内的临时对象析构)
// Destructor (t2析构)
// Destructor (t1析构)
int main(void)
{
B t1 = play(5);
B t2 = play(10);
return 0;
}
// 输出结果1:
// Constructor by parameter 5 (调用带参数的构造函数在fun内生成的临时对象)
// Destructor (5传入fun时生成的临时对象析构)
// Constructor by parameter 10 (调用带参数的构造函数在fun内生成的临时对象)
// Destructor (10传入fun时生成的临时对象析构)
// Destructor (t2析构)
// Destructor (t1析构)
// 输出结果2: (去除#注释部分#)
// Constructor by parameter 5 (调用带参数的构造函数在fun内生成的临时对象)
// Copy constructor (调用复制构造函数把临时对象复制到t1)
// Destructor (fun内的临时对象析构)
// Constructor by parameter 10 (调用带参数的构造函数在fun内生成的临时对象)
// Copy constructor (调用复制构造函数把临时对象复制到t2)
// Destructor (fun内的临时对象析构)
// Destructor (t2析构)
// Destructor (t1析构)