04.构造函数 析构函数 拷贝函数

(创建于2017/12/24)

构造函数的调用
#define  _CRT_SECURE_NO_WARNINGS
#include "iostream"  //包含c++的头文件
using namespace std;
class Me {
public:
    Me() {
        cout << "无参构造"<< endl;
    }

    Me(int a) {
        cout << "有参构造" <
拷贝构造函数的调用
#include "iostream"  //包含c++的头文件
using namespace std;

class Class2 {
public:
    Class2() {
        cout << "class2构造函数" << endl;
    }
    Class2(const Class2 &clazz) {
        cout << "class2拷贝构造函数"<< endl;
    }
};
int main() {
    Class2 class_a;
    //用一个对象初始化另一个对象的时候,拷贝构造函数被编译器调用,
    //如果我们不提供显示的拷贝构造函数,编译器会调用默认的
    Class2 class_b = class_a;

    system("pause");
    return 0;
}
拷贝构造函数和析构函数
#define  _CRT_SECURE_NO_WARNINGS
#include "iostream"  //包含c++的头文件
using namespace std;

class Class2 {
public:
    Class2() {
        cout << "class2构造函数" << endl;
    }
    Class2(const Class2 &clazz) {
        cout << "class2拷贝构造函数"<< endl;
    }
    ~Class2() {
        cout << "class2的析构函数"<< endl;
    }
};

void begin2(Class2 &clas) {
    cout << "begin2"<< endl;
}
void begin3(Class2 clas) {
    cout << "begin3" << endl;
}
void begin() {
    Class2 class_a;
    Class2 class_b = class_a;

    begin2(class_b);
    cout << "--------------------------" << endl;
    begin3(class_b);
}
int main() {
    begin();
    system("pause");
    return 0;
}

打印结果
class2构造函数             //Class2 class_a
class2拷贝构造函数      //Class2 class_b = class_a;
begin2                           //cout << "begin2"<< endl;    begin2传递的是引用,没有执行拷贝构造函数
--------------------------
class2拷贝构造函数      //begin3传递的是对象,直接赋值,拷贝构造函数执行
begin3
class2的析构函数
class2的析构函数
class2的析构函数
请按任意键继续. . .
匿名对象的拷贝和析构

调用get方法,创建了一个Class2对象,并将它返回,此时看打印结果,构造函数执行一次在意料之中,但是为什么拷贝构造函数也会执行,而且析构函数执行了两次,可见这个过程中产生了两个对象。原因在于,函数的返回值是一个元素(对象)的时候,返回的是一个新的匿名对象,而不是真的像get方法中把a返回了,相当于把a赋值给了一个匿名对象,所以拷贝构造函数被调用了一次,产生两个对象,所以销毁两次。

#define  _CRT_SECURE_NO_WARNINGS
#include "iostream"  //包含c++的头文件
using namespace std;

class Class2 {
public:
    Class2() {
        cout << "class2构造函数" << endl;
    }
    Class2(const Class2 &clazz) {
        cout << "class2拷贝构造函数"<< endl;
    }
    ~Class2() {
        cout << "class2的析构函数"<< endl;
    }
};
Class2 get() {
    Class2 a;
    return a;
}
int main() {
    get();
    system("pause");
    return 0;
}
打印结果
class2构造函数
class2拷贝构造函数
class2的析构函数
class2的析构函数
请按任意键继续. . .

然后我们在上边的代码基础上做一点改动,get产生的对象被接收。此时看打印,析构函数只执行了一次。拷贝构造函数执行,将产生的匿名对象通过=号赋值给temp,因为这个新的对象被temp接收,那么get方法执行完成,这个对象没有被销毁,销毁的是a对象

#define  _CRT_SECURE_NO_WARNINGS
#include "iostream"  //包含c++的头文件
using namespace std;

class Class2 {
public:
    Class2() {
        cout << "class2构造函数" << endl;
    }
    Class2(const Class2 &clazz) {
        cout << "class2拷贝构造函数"<< endl;
    }
    ~Class2() {
        cout << "class2的析构函数"<< endl;
    }
};
Class2 get() {
    Class2 a;
    return a;
}
int main() {
    Class2 temp = get();
    system("pause");
    return 0;
}

打印结果
class2构造函数
class2拷贝构造函数
class2的析构函数
请按任意键继续. . .

在上边代码上再做一点改动,我们把temp先初始化,然后接收get产生的对象.可以看到析构函数又执行了两次,这是因为,get产生的新对象是用于初始化了另一个已经存在的对象,然后这个匿名对象就没有用了,会被析构掉,temp已经分配了内存,有了自己的空间,将get通过=号赋值给它只是将get对象的内容拷贝到了temp上,然后get产生对象的空间就没有用了,被释放,而为什么上边的temp没有初始化情况下,get产生的对象不会被析构,因为上边temp只是定义了一个对象,还没有分配空间,接收get对象后相当于指向了这块空间,这块空间有了引用就不会被释放,跟Java垃圾回收机制很像

#define  _CRT_SECURE_NO_WARNINGS
#include "iostream"  //包含c++的头文件
using namespace std;

class Class2 {
public:
    Class2(int a, int b) {

    }
    Class2() {
        cout << "class2构造函数" << endl;
    }
    Class2(const Class2 &clazz) {
        cout << "class2拷贝构造函数"<< endl;
    }
    ~Class2() {
        cout << "class2的析构函数"<< endl;
    }
};
Class2 get() {
    Class2 a;
    return a;
}
int main() {
    Class2 temp(1,2);
    temp = get();
    system("pause");
    return 0;
}

打印结果
class2构造函数
class2拷贝构造函数
class2的析构函数
class2的析构函数
请按任意键继续. . .

浅拷贝存在的问题

C++默认提供的拷贝构造函数在进行拷贝的时候是浅拷贝,对于对象或者说内存中的空间,它拷贝的是地址而不是重新开辟空间,这会导致一些列的问题。下边的程序就是因为浅拷贝导致同一个内存空间被free两次而导致崩溃

#define  _CRT_SECURE_NO_WARNINGS
#include "iostream"  //包含c++的头文件
using namespace std;

class Class2 {
public:
    char *p;
public:
    Class2(const char *name) {
        int len = strlen(name);
        p = (char *)malloc(len + 1);
        strcpy(p, name);
        cout <<"执行函数"<

那么浅拷贝问题如何解决?只需要我们手动的编写拷贝构造函数,单独开辟空间即可

#define  _CRT_SECURE_NO_WARNINGS
#include "iostream"  //包含c++的头文件
using namespace std;

class Class2 {
public:
    char *p;
public:
    Class2(const char *name) {
        int len = strlen(name);
        p = (char *)malloc(len + 1);
        strcpy(p, name);
        cout <<"执行函数"<
构造参数列表

执行顺序,像这种一个类中又套了另一个类,而被嵌套的类又只提供了有参构造,就需要通过构造参数列表对他进行初始化,先执行被组合对象的构造函数,如果组合对象有多个,则按照定义顺序执行构造函数,比如下边,先执行a构造,在执行a2构造,在下边的程序中就是先执行A的构造,然后执行外层B的构造,而析构函数则和构造函数调用顺序相反

#define  _CRT_SECURE_NO_WARNINGS
#include "iostream"  //包含c++的头文件
using namespace std;

class A {
public:
    A(int a) {

    }
};

class B {
public:
    int b;
    A a;
    A a2;

    B(int b):a(1),a2(2){

    }
    B(int a, int b, int c, int d) :a(c), a2(d) {

    }
};

int main() {
    system("pause");
    return 0;
}

C++类的一般写法

.h 头文件声明:

#pragma once  //防止重复循环引用
class MyTeacher
{
public:
    int age; 
    char*name;

public :
    void setAge(int age);
    int getAge();
    void setName(char *name);
    char *getName();
};
如果类属性中有const类型,如何初始化

同样是在形参列表中处理

 #define  _CRT_SECURE_NO_WARNINGS
#include "iostream"  //包含c++的头文件
using namespace std;

class A {
public:
    A(int a) {

    }
};

class B {
public:
    int b;
    A a;
    A a2;
    const int c;
    B(int b):a(1),a2(2),c(3){

    }
    B(int a, int b, int c, int d) :a(c), a2(d),c(3){

    }
};

int main() {
    system("pause");
    return 0;
}

.cpp源文件定义

#include "MyTeacher.h"

#include   //这个头文件和命名空间有一个不存在 cout就无法使用
using namespace std;
void MyTeacher::setAge(int age)
{
    this->age = age;
}

int MyTeacher::getAge()
{
    return this->age;
}

void MyTeacher::setName(char *name)
{
    this->name = name;
}

char *MyTeacher::getName()
{
    return this->name;
}

void main()
{
    MyTeacher t1;
    t1.name = "renzhenming";
    t1.age = 26;
    cout << t1.getName()<< endl;
    system("pause");
}

构造函数 析构函数 拷贝函数

#define _CRT_SECURE_NO_WARNINGS
#include   //这个头文件和命名空间有一个不存在 cout就无法使用
using namespace std;

class MyTeacher
{
public:
    int age;
    char*name;

public:
    MyTeacher() {
        this->name = (char*)malloc(100);
        strcpy(name, "renzhenming");
        age = 26;
        cout << "无参构造函数"  << this->name << endl;
    }

    MyTeacher(char *name, int age) {
        //有参构造函数会覆盖默认的无参构造,同Java
        this->age = age;
        this->name = (char*)malloc(100);
        strcpy(this->name, name);
        cout << "有参构造函数" << this->name< name); //C++的释放方法
        free(this->name);      //C的释放方法
        cout << "析构函数" << endl;

    }
    
    //浅拷贝
    /*MyTeacher(const MyTeacher &obj) {
        //默认的拷贝构造函数就是这样,是值拷贝,属于浅拷贝
        this->name = obj.name;
        this->age = obj.age;
        cout << "拷贝构造函数" << this->name << endl;
    }*/

    //深拷贝(复写默认的拷贝函数)
    //使用深拷贝可以避免浅拷贝的问题,拷贝函数的时候重新开辟空间存放name值,这样,释放空间的时候就会
    //避免一个内存释放两次的问题,

    //浅拷贝(值拷贝),拷贝的是指针的地址
    //深拷贝,拷贝的是指针指向的内容

    //拷贝构造函数何时会调用?
    //1.声明时赋值
    //MyTeacher t2 = t1;
    //2.作为参数传入,实参给形参赋值(都是这种变形:MyTeacher t2 = t1;)
    //func1(t1);
    //3.作为函数返回值返回,给变量初始化赋值
    //MyTeacher t3 = func1(t1);

    MyTeacher(const MyTeacher &obj) {
        //复制name属性
        int len = strlen(obj.name);
        this->name = (char*)malloc(len + 1);//+1是为了结束符 0
        strcpy(this->name, obj.name);
        this->age = obj.age;
        cout << "拷贝构造函数" << this->name << endl;
    }

    void printinfo() {
        cout << name << "," << age << endl;
    }
};

void func() {
    MyTeacher t;
    //MyTeacher t("a",1);
}
MyTeacher func1(MyTeacher t) {
    t.printinfo();
    return t;
}

void func2() {
    //测试浅拷贝存在的问题

    //执行这段代码后,有参构造执行,申请一块空间给name存放传入的name,
    MyTeacher t1("什么问题", 11);
    //执行这段代码后,默认的拷贝函数执行,进行值的拷贝,t2的name也指向了开辟的存放name的那块空间
    //此时,t1的name和t2的name指向了同一块空间
    MyTeacher t2 = t1;
    t2.printinfo();

    //这个func2函数执行完成之后,回收两个变量,回收t1的时候,析构函数执行,free了t1中的name,回收
    //t2的时候,析构函数同样执行,回收name,因为t2的name同样指向那块空间,所以导致那块空间被回收两次,就报错了
}

void main()
{
    cout << "-------------构造函数的调用---------------" << endl;
    //调用无参构造函数
    MyTeacher at;
    //调用有参构造函数
    MyTeacher t("renzhenming",26);
    cout << "-------------析构函数在函数执行完成之后需要回收的时候调用---------------" << endl;
    func();

    cout << "-------------构造函数的另一种调用方式---------------" << endl;
    MyTeacher t2 = MyTeacher("zhangsna", 22);

    cout << "-------------拷贝构造函数---------------" << endl;
    MyTeacher t3("lisi",21);
    MyTeacher t4 = t3;
    t4.printinfo();

    cout << "-------------浅拷贝(默认的值拷贝)存在的问题---------------" << endl;
    func2();
    system("pause");
}
虚析构函数的作用

base中如果不加virtual,那么析构的时候只会执行base的析构函数,加上之后,才能将子类的一块执行,释放空间

#define _CRT_SECURE_NO_WARNINGS
#include 
#include
#include 
class A {

public:
    A() {
        p = new char[20];
        strcpy(p, "A");
        printf("A()\n");
    }
    virtual ~A() {
        delete []p;
        printf("~A()\n");
    }
private:
    char *p;
};

class B : public A{

public:
    B() {
        p = new char[20];
        strcpy(p, "B");
        printf("B()\n");
    }
    virtual ~B() {
        delete[]p;
        printf("~B()\n");
    }
private:
    char *p;
};


class C : public B {

public:
    C() {
        p = new char[20];
        strcpy(p, "C");
        printf("C()\n");
    }
    ~C() {
        delete[]p;
        printf("~C()\n");
    }
private:
    char *p;
};

void deleteBase(A *a) {
    delete a;
}

void main() {
    C *c = new C();
    deleteBase(c);
    system("pause");
}

你可能感兴趣的:(04.构造函数 析构函数 拷贝函数)