复制构造函数
形如className :: className(const &) / className :: className(const className &)后者能以常量对象作为参数
如不写复制构造函数,则编译器默认提供,为了完成对象的复制功能
它起作用有三个工作环境:
1.一个对象初始化另一个同类的对象
1 Simple c2(c1); 2 Simple c2 = c1;//初始化
2.如果某函数有一个参数是类的一个对象,那么该函数被调用时,类的复制构造函数被调用
1 Simple A 2 { 3 pbulic: 4 A(){}; 5 A(A & a) 6 { 7 cout<<.....<<endl; 8 } 9 }; 10 void Fun(A a1){} 11 int main() 12 { 13 A a; 14 Fun(a);//.....
15 return 0; 16 }
3.如果函数的返回值是类的对象时,则函数返回是,类的复制构造函数被调用
1 class Simple 2 { 3 pbulic: 4 int x; 5 Simple(int n){x = n;}; 6 Simple(const Simple & a) 7 { 8 x = a.x; 9 cout<<"Copy constructor called"<<endl; 10 } 11 }; 12 Simple Fun() 13 { 14 Simple b(100); 15 return b;//作为复制构造函数的参数
16 } 17 int main() 18 { 19 cout<<Fun().x<<endl;// Copy constructor called 100
20 return 0; 21 }
注意:对象间的赋值不导致复制构造函数的调用
A c1,c2;
c2 = c1 ;//不会调用
A C3(C1);//这是初始化操作,会调用赋值构造函数
在函数参数列表中,为节省开销,不引发复制构造函数的调用,使用ClassName & 引用类型为参数,
又为了确保实参值不变,再在引用前加上const关键字。复制构造函数往往加const和&,具体可以点这里
类型转换构造函数
它的目的是实现类型的自动转换,并且只有一个参数。当需要的时候,编译系统会自动调用转换构造函数,建立
一个无名的临时对象(或临时变量)。
1 class Complex 2 { 3 public: 4 double real, imag; 5 Complex( int i) 6 {//类型转换构造函数
7 cout << "IntConstructor called" << endl; 8 real = i; imag = 0; 9 } 10 Complex(double r,double i) {real = r; imag = i; } 11 }; 12 int main () 13 { 14 Complex c1(7,8); 15 Complex c2 = 12; 16 c1 = 9; // 9被自动转换成一个临时Complex对象
17 cout << c1.real << "," << c1.imag << endl; 18 return 0; 19 }
1 class Complex 2 { 3 public: 4 double real, imag; 5 explicit Complex( int i) 6 {//显式类型转换构造函数
7 cout << "IntConstructor called" << endl; 8 real = i; imag = 0; 9 } 10 Complex(double r,double i) {real = r; imag = i; } 11 }; 12 int main () 13 { 14 Complex c1(7,8); 15 Complex c2 = Complex(12); 16 c1 = 9; // error, 9不能被自动转换成一个临时Complex对象
17 c1 = Complex(9) //ok
18 cout << c1.real << "," << c1.imag << endl; 19 return 0; 20 }
析构函数:~类名(){}
它没有返回值和参数,一个类对应一个析构函数,在类的对象消亡时自动调用,也可以手动调用进行对象消亡的善后工作。如果懒的去写,编译器自动会生成缺省析构函数,但是什么也不做,如果写了,就不自动生成了。
1 class Simple 2 { 3 private : 4 char * p; 5 public: 6 Simple() 7 { 8 p = new char[10]; 9 } 10 ~ Simple () ; 11 }; 12 Simple ::~ Simple() 13 { 14 delete [] p; 15 }
在对一个对象进行delete时,调用一次析构函数,在delete[]对象数组时,会调用n次析构函数,取决于数组大小。此外:
1 class CMyclass 2 { 3 public: 4 ~CMyclass() { cout << "destructor" << endl; } 5 }; 6 CMyclass obj; 7 CMyclass fun(CMyclass sobj ) 8 { //参数对象消亡也会导致析 9 //构函数被调用
10 return sobj; //函数调用返回时生成临时对象返回
11 } 12 int main() 13 { 14 obj = fun(obj); //函数调用的返回值(临时对象)被
15 return 0; //用过后,该临时对象析构函数被调用
16 } 17 //destructor形参对象消亡 18 //destructor临时对象消亡 19 //destructor全局对象消亡
new出来的对象或对象数组,不delete就不会消亡,就不会调用析构函数!!不是new出来的,则在生命期结束会自动调用
关于引用:引用是C++有的,C所没有的,标志为 &,即C的取地址符。它的声明为这样
1 int n = 4; 2 int & r = n; //r为 int & 类型
其中r和n指向同一片地址空间 ,对 r和n的修改都会改变原来是4的那个值,相当于为n取了个别名r
在定义引用时一定要将其初始化成引用某个变量。并且之后不会引用别的变量
只能引用变量,不能引用常量和表达式
一个简单而又形象的,见得次数很多的例子:交换元素
1 void swap( int * a, int * b) 2 { 3 int tmp; 4 tmp = * a; 5 * a = * b; 6 * b = tmp; 7 } 8 int n1, n2; 9 swap(& n1,& n2) ; // n1,n2的值被交换
1 //用了引用 2 void swap( int & a, int & b) 3 { 4 int tmp; 5 tmp = a; 6 a = b; 7 b = tmp; 8 } 9 int n1, n2; 10 swap(n1,n2) ; // n1,n2的值被交换
1 //en ..交换还可以这样写,当然远不如swap(a,b)写的快哈
2 int a = 3; 3 int b = 5; 4 a = a + b; 5 b = a - b; 6 a = a - b; 7 cout<<a<<" "<<b<<endl; 8 a = a ^ b; 9 b = a ^ b; 10 a = a ^ b; 11 cout<<a<<" "<<b<<endl;
引用还可以作为函数的返回值
1 int n = 4; 2 int & SetValue() 3 { 4 return n; 5 } 6 int main() 7 { 8 SetValue() = 40; 9 cout << n;return 0; 10 } //输出: 40
关于常引用:
int n;
const int & r = n;
r = 10;//error 不能通过常引用r改变n的值
n = 30;//n的值可以变化
const T & 和T & 是不同的类型!!!
T & 类型的引用或T类型的变量可以用来初始化const T & 类型的引用。
int n = 8;
int & r1 = n;
const int r2 = r1;//初始化const T & 类型的引用。
const T 类型的常变量和const T & 类型的引用则不能用来初始化T &类型的引用,
除非进行强制类型转换
常量指针:
1 //1.
2 int n,m 3 const int * p = n;//常量指针
4 *p = 5//error 不可以通过修改常量指针修改指向的内容
5 n = 5;//ok
6 p = & m;//ok 常量指针可以指向别的内容 7
8 //2.不可以把常量指针赋值给非常量指针(强转可以),反之可以
9 const int * p; 10 int * p2; 11 p1 = p2; //ok
12 p2 = p1; //error
13 p2 = (int * ) p1; //ok,强制类型转换
指针常量:
1 int a,b; 2 int const *p = & a; 3 * p = 9;//通过指针修改所指向的内容,ok
4 p = & b;//error 不可更改所指向的地址
这两个不要搞混淆了。。。。
简单的记忆,抓住第一个是常量在前,就意味着整个后面的地址内容是个常量,不可以通过指针去修改它,但是指向谁是可以变的;指针常量,这个常量在指针后面,表明这个指针所指向的地址不能变了,但你可以用它修改地址所对应的内容。。
动态内存分配:
变量: int * px = new int; //大小为sizeof(int)
数组: int * pn = new int[20];
释放动态分配的内存:
int * px = new int;
delete px;//只能删除一次
//数组
int * pn = new int[20];
delete [] pn;