C++的简单总结(复制构造函数,深拷贝,前拷贝,默认属性)

类的三大属性:

private,public,protected

1,对于类的成员变量或者函数,缺省即为私有

#include <iostream>
using namespace std;

class A
{
        int y;                      //私有成员
        int x;                      //私有成员
        public:
                A(int xx, int yy)     {x = xx; y = yy;}
                void setx(int m)      {x = m;}
                void sety(int n)      {y = n;}
};

int main()
{
        A a(1,2);
}

2,对于类的继承而言,缺省也是私有继承!!!!!

#include <iostream>
using namespace std;

class A
{
        int y;
        int x;
        public:
                A(int xx, int yy)     {x = xx; y = yy;}
                void setx(int m)      {x = m;}
                void sety(int n)      {y = n;}
};

class B:A
{
        int x;
        public:
        B(int xx, int yy,int xxx):A(xx,yy)
        {       x = xxx;             }
};

int main()
{
        B b(1,2,3);
}

如上,B也是同样的私有继承A了。。。。

3,内联函数

内联函数是一种特殊的函数,本身比较少,运行起来也特别快,,由于函数调用会产生一定的调用语句,参数的

传递,入栈出栈,那么当这个函数被反复调用的时候,就会产生很大的开销。于是就产生了内联函数。

当我们的编译器看到了关键字:inline的时候,那么编译器会把函数的实现全部插入到调用内联函数出,这样就

不会产生额外的调用开销了

内联成员函数:

1,inline + 成员函数

2,整个函数体出现在定义类的内部

#include <iostream>
using namespace std;

class A
{
        int y;
        int x;
        public:
                A(int xx, int yy)     {x = xx; y = yy;}
                void setx(int m)      {x = m;}                    //为内联函数,函数的定义在class中
                inline void sety(int n);                            //同样的为内联函数,通过inline声明
};

void A::sety(int n)
{
        y = n;
}

int main()
{
        A a(1,2);
}

内联函数,从代码层看,有函数的结构,而编译后却没有函数的性质,不是在调用时候发生控制转移,而是

在编译的时候,将函数体嵌入到每一个调用之处。

3,成员函数的重载以及参数缺省

我们知道函数是可以重载的,不同的返回值类型或者函数的个数是不相同的,那么就是函数的重载,同样:成员

函数也是可以重载的。成员函数也是可以带有缺省的参数的。C++语法规定,函数在写的时候可以有缺省值,那么

在调用的时候,如果我们没有给参数,那么参数就是缺省值。

#include <iostream>
using namespace std;

class A
{
        int y;
        int x;
        public:
                void init(int x = 0, int y = 0);          //缺省参数
                void setx(int m)      {x = m;}           //下面这两个函数为setx函数的重载
                int  setx()     {return x;}
};

int main()
{
        A a(1,2);
}

这里我们需要注意的就是使用:缺省参数时和函数重载时的二异行,,,,,

如下:程序

#include <iostream>
using namespace std;

class A
{
        int y;
        int x;
        public:
                void init(int xx = 0, int yy = 0);          //缺省参数
                void setx(int m =0)      {x = m;}
                int  setx()     {return x;}
};

int main()
{
        A a(1,2);
        a.setx();
}

如上的setx函数,a调用setx函数,就不知道调用的是哪一个,就会产生编译出错,,,,:

5,构造函数

一:成员函数的一种,名字与类名相同,可以有参数,不能有返回值(任何类型都不行,包括void)

二:作用是对类对象进行初始化,如:给成员变量赋初值

三:如果定义类的时候没有写构造函数,则编译器生成一个默认的无参构造函数(不做任何操作)

四:如果我们定义了构造函数,那么编译器不会生成默认的无参构造函数

五:对象生成时构造函数自动被调用(一定),对象一旦生成,就再也不能在其上执行构造函数

六:一个类中可以有多个构造函数,构造函数的重载,那么如果要调用那个构造函数,需要看我们的对象是如何

调用的

那么为什么需要构造函数呢????

构造函数执行必要的初始化工作,有了构造函数就不必在专门写必要的初始化函数了,也不用担心忘记了写

初始化函数了,有时候对象没有被初始化就使用,那么就会导致编译错误了。。。。

#include <iostream>
using namespace std;

class A
{
        int y;
        int x;
        public:
                void init(int xx = 0, int yy = 0);          //缺省参数
                void setx(int m =0)      {x = m;}
};
//编译器自动的调用默认构造函数
int main()
{
        A a;            //默认的构造函数被调用
        A *pc = new A;  //默认的构造函数被调用
        pc->init(1,2);
}

如上,只要有对象生成,就一定会调用构造函数

构造函数的重载及其调用问题:

#include <iostream>
using namespace std;

class A
{
        double y;
        double x;
        public:
                A(double xx, double yy)
                {    x = xx; y = yy;}
                A(double xxx)
                {    x = xxx;}
                void setx(int m =0)      {x = m;}
};
//构造函数的重载
int main()
{
        A a(2,3);           //调用的是第一个构造函数,整形被转化成了double类型
        A b(2);             //调用的是第二个构造函数,同样的被转化成了double
}

构造函数在数组(对象数组)中的使用:

#include <iostream>
using namespace std;

class A
{
        double x;
        public:
                A()
                {     cout<<"Construction 1"<<endl; }
                A(double xx)
                {     cout<<"Construction 2"<<endl; x = xx;}
                void setx(int m =0)      {x = m;}
};

int main()
{
        A a1[2];     //调用无参的构造函数
        cout<<"step1"<<endl;
        A a2[2] = {4,5};  //调用有参的构造函数初始化
        cout<<"step2"<<endl;
        A a3[2] = {3};    //一个调用有参的构造函数,一个调用无参的构造函数
        A *p = new A[2];  //调用无参的构造函数                                                                                                  

        delete[] p;
}

运行结果:

如上:这里还是有很多不同的,,,,,,生成一个对象就一定要调用构造函数

还有一种特殊的例子,我们来看一下:

#include <iostream>
using namespace std;

class A
{
        double x;
        double y;
        public:
                A()                                            //无参构造函数
                {     cout<<"Construction 1"<<endl; }
                A(double xxx)                        //一个参数构造函数
                {     cout<<"Construction 2"<<endl; x = xxx;}
                A(double xx, double yy)      //两个参数构造函数
                {     cout<<"Construction 3"<<endl; x = xx; y = yy;}
                void setx(int m =0)      {x = m;}
};

int main()
{
        A a[3] = {1, A(2,3)};           //定义了一个对象数组,第一个一个元素初始化,第二个两个元素,第三个没有初始化
        A *p[3] = {new A(3), new A, new A(4,5)};
 }

运行结果:

从上面可以看出,我们的对象数组是这个样子进行初始化的。。。。。。。。。。。并且也是这个样子调用new的。

其实总的来说:

对于对象数组的初始化:嗯嗯,好像只能用这种方式:

A a[3] = {A(1), A(2,3)};
A *p[3] = {new A(3), new A, new A(4,5)};

嗯嗯,就是,这种方式:上面我们实现的方式是不一样的,也就是说:当只有一个参数的时候

A(1)和1是一样的,,,,,,,,,,,

即就是:

A a = {1},A a(1),A a = {A(1)};      //三者是一样的!!!!!

6,复制构造函数(Copy  Constructor)

我们经常使用函数,传递各种各样的参数,而我们今天要说的复制构造函数:就是把对象(注意是对象,不是

对象的指针或者对象的引用)作为一个参数,并且传递给函数

我们也知道,把参数传递给函数有三种方法:值传递,地址传递,传引用。

值传递时,传送的是作为实际参数的对象的副本而不是实际对象本身。而且这个副本的内容是按位从原始参数

那里复制过来的,内容是相同的。

当传递过来的时候,如果是类的对象呢???会不会触发类的构造函数呢,,调用结束,会不会触发析构呢???

从程序看:

#include <iostream>
using namespace std;

class A
{
        int x;
        public:
                A(int x1)
                {     x = x1;    cout<<"Constructor"<<endl; }       //构造函数的实现
                ~A()
                {     cout<<"Destructor"<<endl;             }       //析构函数的实现
                void print()
                {     cout<<x<<endl;                        }
};

void f(A a)
{
        a.print();
}

int main()
{
        A a(10);
        f(a);
        a.print();

        return 0;
}

运行结果:

从运行结果可以看到:A类的构造函数只被调用了一次,发生在创建对象a的时候,而A类的析构函数却被调用了两次

这是为什么呢???

查阅书籍后发现:如同我们上面所提到的,把一个对象作为参数传递给函数时,同时创建了该对象的副本(这

个副本将作为函数的参数)。也就是说:创建了一个新的对象。当函数结束时,作为函数的实际参数的副本将被

销毁,这产生了两个有趣的问题。。。。。。。

1,在创建对象的副本时有没有调用构造函数

2,在销毁对象的副本时有没有调用了析构函数

首先,在调用函数时,程序创建了一个对象的副本作为形式参数,此时普通的构造函数并没有被调用,而是调用

了复制构造函数,复制构造函数定义了如何创建了一个对象的副本。。。。。如果一个类中没有显示的地定义类

的复制构造函数,那么C++将提供一个默认的复制构造函数。默认的复制构造函数将以按位复制的形式创建一个

对象的副本,即创建一个与原对象一样的副本。

由于普通的构造函数通常用于初始化对象的某些成员,因此就不能调用普通的构造函数创建对象的副本,因为

这样产生的对象可能与现有的对象的状态属性不完全相同。很简单,只是因为当把一个对象传递给函数时,需要

使用的是对象的当前状态,而不是初始状态。。。。

其次,当函数结束时,由于作为参数的对象副本超出了作用域,因此它将被销毁,从而调用了析构函数,这就是

为什么前面的程序中调用了两次析构函数的原因,第一次函数f()参数超出了作用域,第二次是因为main函数

中的对象a在程序结束时被销毁。。。。

综上所述:当创建一个对象的副本作为函数参数时,普通的构造函数没有被调用,所调用的构造函数是按位复制

的默认复制构造函数。但是,当对象的副本被销毁时(通常因为,函数返回而超出了作用域),析构函数被调用

,如果析构函数是空的话,通常不会发生什么问题,但是一般情况下:析构函数都要完成一些清理工作(释放

内存。。。。)

假如,我们真的在复制构造函数里面为一个指针变量分配了内存,在析构函数里释放了分配给这个指针所指向的

空间,那么我们可以想想,在把对象传递给函数到整个main函数返回的时候,发生了什么???

首先,有一个对象的副本产生了,这个副本也有一个指针,,,它和原始对象的指针指向同块内存空间,,函数

返回时,副本对象的析构函数被执行了,,,释放了对象副本里指针所执行的内存空间,但是这个内存空间对于

原始对象来说,还是有用的啊,,,,(干嘛就直接给释放了呢???),就程序本身而言,这是一个错误,

但是错误还是没有停止,,,等到main函数结束的时候,原始对象的析构函数继续被执行,,,对同一块内存

释放两次,这有可能加深错误!!!

那么,如何解决上面的问题呢???

嗯嗯,可以用传地址,传引用,这样却是可以合理的解决这样一个问题,但是,有的时候,我们不需要直接的改

变外部变量啊,,,那如何处理呢????

其实,这个时候,我们可以用复制构造函数来解决这个问题,复制构造函数是在产生对象副本的时候执行的,

我们可以调用自己的复制构造函数。在自己的复制构造函数里,我们可以申请一个新的内存空间来保存那个

构造函数所指向的内容。。。。这样,在执行对象副本的时候,执行的就是刚刚申请的那个空间了。。。

复制构造函数(拷贝构造函数)

X&
const X&
volatile X&
const volatile X&
//后面也可以跟其他的参数。。。。。。。。。。。。。

如上,就是复制构造函数的几种类型。。。。。。。。。

1, 当一个对象副本被作为参数传递给函数时

display(y);

2, 当一个对象被另一个对象显式的初始化时

my_string x = y;

3, 当创建一个临时对象时(最常见的是:作为函数的返回值)

y = func2();

前面2中情况,对象y的引用将被传递给复制构造函数;在第三种情况下,函数func2()返回的对象引用将被传递给

y的复制构造函数。。。。

关于对象之间的显示初始化。。。

#include <iostream>
using namespace std;

class A
{
        int x;
        public:
                A(int x1)
                {     x = x1;    cout<<"Constructor"<<endl; }       //构造函数的实现
                ~A()
                {     cout<<"Destructor"<<endl;             }       //析构函数的实现
                void print()
                {     cout<<x<<endl;                        }
};

int main()
{
        A a(10);
        A b = a; //  可以这样初始化
        A c(a);   //  也可以这样初始化
        return 0;
} 

运行结果:

其实后面两个的初始化调用的是复制构造函数,只是在当前类中我们没写,调用的是默认,析构调用了 ,,,,

程序:

#include <iostream>
using namespace std;

class A
{
        int x;
        public:
                A(int x1)
                {     x = x1;    cout<<"Constructor"<<endl; }       //构造函数的实现
                A(A & a)                                            //自定义的复制构造函数
                {     x = a.x;   cout<<"自定义的 Constructor"<<endl;}
                ~A()
                {     cout<<"Destructor"<<endl;             }       //析构函数的实现
                void print()
                {     cout<<x<<endl;                        }
};

int main()
{
        A a(10);
        A b = a;
        A c(a);
        return 0;
}

程序如上:我们写了自定义的复制构造函数

运行结果:

为了明显的区分开跟赋值的区别,我们重载了一个赋值运算符:

#include <iostream>
using namespace std;

class A
{
        int x;
        public:
                A(int x1)
                {     x = x1;    cout<<"Constructor"<<endl; }       //构造函数的实现
                A(A & a)                                            //自定义的复制构造函数
                {     x = a.x;   cout<<"自定义的 Constructor"<<endl;}

                A &operator = (A & a)                               //重载了一个赋值运算符
                {     this->x = a.x;  cout<<"赋值运算符的重载"<<endl;}
                ~A()
                {     cout<<"Destructor"<<endl;             }       //析构函数的实现
                void print()
                {     cout<<x<<endl;                        }
};

int main()
{
        A a(10);
        A b(2);
        b = a;
        return 0;
}

运行结果:

1,上面我们知道了一些关于复制构造函数里面的一些东西。。。。那么我们有一些疑惑???

复制构造函数可以重载吗????

可以的,因为上面我们提到了,复制构造函数的类型;;;;;;;;

#include <iostream>
using namespace std;

class A
{
        int x;
        public:
                A(int x1)
                {     x = x1;    cout<<"Constructor"<<endl; }       //构造函数的实现
                A(A & a)                                            //自定义的复制构造函数
                {     x = a.x;   cout<<"自定义的 Constructor"<<endl;}
                A(A & a, int b)
                {     a.x = b;   cout<<"重载的复制构造函数"<<endl;  }
                ~A()
                {     cout<<"Destructor"<<endl;             }       //析构函数的实现
                void print()
                {     cout<<x<<endl;                        }
};

int main()
{
        A a(2);
        A b(a, 3);
        return 0;
}

如上:我们重载了复制构造函数....

运行结果:

2,拷贝构造函数中可以调用private成员变量吗???

从上面的程序中可以看到,这里的调用无压力,,,,可以直接调用,,,因为是一种特殊的构造函数

操作的是自己的成员变量,不用考虑private。。。。。

关于深拷贝,浅拷贝

其实说白了:编译器默认的拷贝构造函数其实就是一个浅拷贝,如同我们上面所提到的那样,如果有指针变量的

话,那么就会出错,因为拷贝后两个指针指向了同一块空间,释放两次自然就会出错。。。。而深拷贝,不但对指针

进行拷贝,也对指向的内容进行拷贝。。。。。。。拷贝后,是两个指向不同地址的指针。。。。。。

1,那么浅拷贝会出现什么问题呢????

int main()
{
        A a(2);
        A b = a;
//      A c(a);   上面这两种情况都是可以的
        return 0;
}

假设类有一个成员变量指针:char *p;

1,浅拷贝只是拷贝了指针,使得两个指针指向同一个地址,这样在对象块结束,调用函数析构的时,会造成同一

份资源析构2次,即delete同一块内存2次,造成程序崩溃。

2,浅拷贝使得b.p和a.p指向同一块内存,任何一方的变动都会影响到另一方

3,结果是会调用两次析构函数,造成同一块内存释放两次,明显的错误

如下:

#include <string.h>
#include <iostream>
using namespace std;

class A
{
        char *p;
        public:
                A(char *s)             //构造函数
                {
                        cout<<"Constructor"<<endl;
                        p = new char[strlen(s) + 1];
                        strcpy(p, s);
                }
                ~A()                   //析构函数
                {
                        if(p != NULL)
                        {
                                delete p;
                                p = NULL;
                                cout<<"析构函数"<<endl;
                        }
                }
};

int main()
{
        char p[] = "abc";
        A a(p);
        return 0;
}

运行结果:

一块空间的多次释放,自然而然的造成了错误。 ,,,,,,,,,,,,,,

深拷贝采用了在堆内存中申请新的空间来存储数据,在我们重新书写的复制构造函数里我们重新为这个新的指针

分配了一块存储空间了,,,,,,,,,,

#include <string.h>
#include <iostream>
using namespace std;

class A
{
        char *p;
        public:
                A(char *s)             //构造函数
                {
                        cout<<"Constructor"<<endl;
                        p = new char[strlen(s) + 1];
                        strcpy(p, s);
                }
                A(A & a)
                {
                        cout<<"复制Constructor"<<endl;
                        int length = strlen(a.p);
                        p = new char[length + 1];
                        strcpy(p, a.p);
                }
                ~A()                   //析构函数
                {
                        if(p != NULL)
                        {
                                delete p;
                                p = NULL;
                                cout<<"析构函数"<<endl;
                        }
                }
};

int main()
{
        char p[] = "abc";
        A a(p);
        A b = a;
        return 0;
}

可以看到:我们重新写了复制构造函数,并且在其中重新分配了内存空间了。。。。

运行结果:

时间: 2024-10-12 01:27:39

C++的简单总结(复制构造函数,深拷贝,前拷贝,默认属性)的相关文章

C++ Primer 学习笔记_54_类与数据抽象 --复制构造函数、赋值操作符

复制控制 --复制构造函数.赋值操作符 引言: 当定义一个新类型时,需要显式或隐式地指定复制.赋值和撤销该类型的对象时会发生什么– 复制构造函数.赋值操作符和析构函数的作用!      复制构造函数:具有单个形参,该形参(常用const修饰)是对该类类型的引用.当定义一个新对象并用一个同类型的对象对它进行初始化时,将显式的使用复制构造函数:当将该类型的对象传递给函数或者从函数返回该类型的对象时,将隐式使用复制构造函数.     析构函数:作为构造函数的互补,当对象超出作用域或动态分配的对象被删除

C++自学笔记_复制构造函数_《C++ Primer》

在内置数据类型中,一般可以用一个变量初始化另一个变量.同样,对于类类型的对象,也可以用一个对象初始化另一个对象,编译器会合成一个复制构造函数. #include <iostream> using namespace std; class Point{ public: Point(int x=0,int y=0):xPos(x),yPos(y){} void printPoint(){ cout<<"xPos:"<<xPos<<endl;

【C/C++】复制构造函数、深复制浅复制

常见问题 Q1. 下面代码的输出结果是( )? 1 #include <iostream> 2 using namespace std; 3 4 class MyClass 5 { 6 public: 7 MyClass(int n) { number = n; } 8 MyClass(const MyClass &other) 9 { 10 number = other.number; 11 cout << "a "; 12 } 13 private:

笔记十:复制构造函数、深拷贝、浅拷贝

复制构造函数 定义: 只有单个形参,而且该形参是对本类类型对象的引用(常用const修饰),这样的构造函数成为复制构造函数.复制构造函数可用于: 1.根据另一个同类型的对象显示或隐式初始化一个对象 2.复制一个对象,将它作为实参传递给一个函数 3.从函数返回时复制一个对象 4.初始化顺序容器中的元素 5.根据元素初始化列表初始化数组元素 --以上定义来自<C++ Primer 中文版 第4版> 浅拷贝/浅复制 第一条中,若一个自定义类对象已经初始化了,并且用该类去初始化另一个同类类型的对象,假

【转】 c++拷贝构造函数(深拷贝,浅拷贝)详解

c++拷贝构造函数(深拷贝,浅拷贝)详解 2013-11-05 20:30:29 分类: C/C++ 原文地址:http://blog.chinaunix.net/uid-28977986-id-3977861.html 一.什么是拷贝构造函数      首先对于普通类型的对象来说,它们之间的复制是很简单的,例如: int a=100; int b=a; 而类对象与普通对象不同,类对象内部结构一般较为复杂,存在各种成员变量.  下面看一个类对象拷贝的简单例子. #include<iostream

【转载】C++拷贝构造函数(深拷贝,浅拷贝)

对于普通类型的对象来说,它们之间的复制是很简单的,例如:int a=88;int b=a; 而类对象与普通对象不同,类对象内部结构一般较为复杂,存在各种成员变量.下面看一个类对象拷贝的简单例子. #include <iostream>using namespace std;class CExample {private:     int a;public:     CExample(int b)     { a=b;}     void Show ()     {        cout<

关于C++类中的土著民:构造函数,复制构造函数,析构函数

我们初学C++时可能会对类的构造函数,复制构造函数,析构函数有点疑问.整理如下(个人见解,如有错误,还望指正.): 1.构造函数 根据构造函数的定义知它的作用是初始化类的数据成员或内嵌类的对象,所以它的参数表就应该是它要初始化的对象类型.构造函数分三类:默认构造函数.构造函数.委托构造函数. 默认构造函数 默认构造函数没有返回值,没有参数表,没有函数体,如果类内没有显式的定义构造函数,系统会自动生成默认构造函数,如果已经定义了构造函数,但仍需要默认构造函数,可以在默认构造函数参数表后加defau

函数重载与复制构造函数

函数重载与复制构造函数   一.函数重载 1.普通函数重载 用main函数多次重复调用一个相同名字但是不同类型的函数来处理不同类型的数据. 如 void func(int); void func(double); float func(float); void func(double); 2.成员函数的重载 我们可以将函数的重载推广到类的成员函数. Class  boy { Public: void  sum(); void  sum(int  x, int  y); } 二.函数的默认参数 在

复制构造函数(拷贝构造函数)

也许很多C++的初学者都知道什么是构造函数,但是对复制构造函数(copy constructor)却还很陌生.对于我来说,在写代码的时候能用得上复制构造函数的机会并不多,不过这并不说明复制构造函数没什么用,其实复制构造函数能解决一些我们常常会忽略的问题. 为了说明复制构造函数作用,我先说说我们在编程时会遇到的一些问题.对于C++中的函数,我们应该很熟悉了,因为平常经常使用:对于类的对象,我们也很熟悉,因为我们也经常写各种各样的类,使用各种各样的对象:对于指针的操作,我们也不陌生吧?嗯,如果你还不