欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

C++复制构造函数,类型转换构造函数,析构函数,引用,指针常量和常量指针

程序员文章站 2024-01-23 14:54:52
复制构造函数形如className :: className(const &) / className :: className(const className &)后者能以常量对象作为参数如不写复制构造函数,则编译器默认提供,为了完成对象的复制功能 它起作用有三个工作环境: 1.一个对象初始化另一 ......

复制构造函数
形如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;