C++学习 --- 类和对象之对象的初始化和清理

二、对象的初始化和清理

1、构造函数和析构函数

#include <iostream>
#include <string>
using namespace std;
​
//对象初始化和清理
//1.构造函数 进行初始化操作
class Person {
public:
    //1、构造函数:
    /* 
        1.没有返回值 不写void
        2.函数名与类名相同
        3.构造函数可以有参数,可以发生重载
        4.创建对象的时候,构造函数会自动调用,而且之调用一次
    */
    Person() {
        cout << " Person构造函数的调用 " << endl;
    }
​
​
    //2、析构函数 进行清理的操作
    /*
        1.没有返回值,不写void
        2.函数名和类名相同  在名称前加~
        3.析构函数不可以有参数的,不可以发生重载
        4.对象在销毁前 会自动调用析构函数,而且只会调用一次
    */
    ~Person() {
        cout << " Person 析构函数调用 " << endl;
    }
};
​
//构造和析构都是必须有的实现
//如果自己不提供,编译器会提供一个空实现的构造和析构
void test01(){
    Person p;  //栈上的数据,test01执行完毕后,释放这个对象
}
​
int main() {
    //test01();
    Person p;
    system("pause");
    return 0;
}

 

2、构造函数的分类及调用
#include <iostream>
#include <string>
using namespace std;
​
//构造函数的分类及调用
//分类
//按照参数分类  无参(默认)构造和有参构造
//按照类型分类  普通构造函数和拷贝构造函数
class Person {
public:
    Person() {
        cout << " Person 的无参构造函数调用 " << endl;
    }
    Person(int a) {
        age = a;
        cout << " Person 的有参构造函数调用 " << endl;
    }
    //拷构造函数
    Person(const Person &p) {
        //将传入的对象属性拷贝到当前对象上
        age = p.age;
        cout << " Person 的拷贝构造函数调用 " << endl;
    }
    ~Person() {
        cout << " Person 的析构函数调用 " << endl;
    }
​
    int age;
};
//调用
void test01() {
    //1.括号法,常用
    Person p1;           //默认构造函数调用
    Person p2(10);       //有参构造函数
    Person p3(p2);         //拷贝构造函数
    cout << "p2 的年龄 :" << p2.age << endl;
    cout << "p3 的年龄 :" << p3.age << endl;
​
    //注意事项1
    //调用默认构造函数的时候,不要加()
    //当写了()之后就不会创建对象了
    //下面这行代码,编译器会认为是函数声明,不会认为在创建对象
    //Person pX(); //无参数,返回值是Person 
//2.显示法
    Person p4;              //无参构造函数
    Person p5 = Person(20); //有参构造函数
    Person p6 = Person(p5); //拷贝构造函数
//匿名对象,特点:当前行执行结束后,系统会立即回收掉匿名对象
    Person(10); 
    cout << "aaa" << endl;
​
    //注意事项2
    //不要利用拷贝构造函数,初始化匿名对象
    //编译器会认为Person(p4) 等价于 Person p4,p4是对象的声明;
    //编译器报错为 Person p4 重定义
    //Person(p4);
//3.隐式转换法
    Person p7 = 15;     //相当于 Person p7 = Person(15);
    Person p8 = p7;     //拷贝构造
}
int main() {
    test01();
​
    system("pause");
    return 0;
}

 

3、拷贝构造函数调用时机

#include <iostream>
#include <string>
using namespace std;
//拷贝构造函数的调用时机
//1.使用一个已经创建完毕的对象来初始化一个新对象
//2.值传递的方式给函数参数传值
//3.用值的方式返回局部的对象
class Person {
public:
    Person() {
        cout << "Person 的无参(默认)构造函数调用 " << endl;
    }
    Person(int age) {
        m_Age = age;
        cout << "Person 的有参构造函数调用 " << endl;
    }
    Person(const Person & p) {
        m_Age = p.m_Age;
        cout << "Person 的拷贝构造函数调用 " << endl;
    }
    ~Person() {
        cout << "Person 的析构函数调用 " << endl;
    }
​
    int m_Age;
};
​
//1.使用一个已经创建完毕的对象来初始化一个新对象
void test01() {
    Person p1(20);  //无参构造函数调用
    Person p2(p1);  //拷贝构造函数调用
    cout << "p2的年龄:" << p2.m_Age << endl;
}
​
//2.值传递的方式给函数参数传值
void doWork(Person p) {
​
}
void test02() {
    Person p;        //无参构造函数调用
    doWork(p);       //拷贝构造函数调用
}
​
//3.用值的方式返回局部的对象
Person doWork2() {
    Person p1;
    cout << (int *)&p1 << endl;
    return p1;
}
void test03() {
    Person p = doWork2();  //拷贝构造函数调用
    cout << (int *)&p << endl;
}
​
int main() {
    //test01();
    //test02();
    test03();
​
    system("pause");
    return 0;
}

 

4、构造函数的调用规则

#include <iostream>
#include <string>
using namespace std;
//构造函数的调用规则
//1.创建一个类,c++编译器会给每个类都添加至少3个函数
//默认构造 (空实现)
//析构函数 (空实现)
//拷贝构造 (值拷贝)
//2.
//如果我们写了 有参构造函数,编译器就不在提供默认构造(不能使用),依然提供拷贝构造(能使用)
//如果我们写了拷贝构造函数,编译器就不再提供其他普通构造函数了
class Person {
public:
    /*Person() {
        cout << "Person 的 无参(默认)构造函数调用" << endl;
    }*/
    Person(int age) {
        m_Age = age;
        cout << "Person 的 有参构造函数调用" << endl;
    }
    //Person(const Person & p) {
    //  m_Age = p.m_Age;
    //  cout << "Person 的 拷贝构造函数调用" << endl;
    //}
    ~Person() {
        cout << "Person 的 析构函数调用" << endl;
    }
    int m_Age;
};
​
//void test01(){
//  Person p;
//  p.m_Age = 18;
//
//  Person p2(p);
//  cout << "p2 的年龄:" << p2.m_Age << endl;
//}
void test02() {
    Person p(20);
    Person p2(p);
    cout << "p2 的年龄:" << p2.m_Age << endl;
}
​
int main() {
    //test01();
    test02();
    system("pause");
    return 0;
}

 

5、深拷贝与浅拷贝

编译器提供的拷贝构造函数是浅拷贝,对于有指针类型的成员,在调用浅拷贝构造函数和析构函数后有重复释放指针的问题,出现异常。需要自己实现深拷贝构造函数重新开辟堆区空间解决此问题。

如果属性有在堆区开辟的,一定要自己提供拷贝构造函数,防止浅拷贝带来的问题

#include <iostream>
#include <string>
using namespace std;
​
class Person {
public:
    
    Person() {
        cout << "Person 的默认(无参)构造函数调用 " << endl;
    }
    
    Person(int age,int height) {
        m_Age = age;
        m_Height = new int(height);
        cout << "Person 的有参构造函数调用 " << endl;
    }
    
    //自己实现拷贝构造函数解决浅拷贝带来的问题
    Person(const Person &p) {
        cout << "Person 的拷贝构造函数调用 " << endl;
        m_Age = p.m_Age;        //编译器默认实现的部分
        //原来的浅拷贝
        //m_Height = p.m_Height; //编译器默认实现的部分
        //深拷贝
        m_Height = new int(*p.m_Height);
    }
    
    //析构规则,先进后出。先定义的变量最后调用析构
    ~Person() {
        //堆区开辟的数据做释放操作
        if (m_Height != NULL) {
            delete m_Height;
            m_Height = NULL;
        }
        
        cout << "Person 的析构函数调用 " << endl;
    }
    
    int m_Age;
    int * m_Height; //指针表示身高,开辟在堆区
};
​
void test01() {
    Person p1(18,160);
    cout << "p1 的年龄: " << p1.m_Age << endl;
    cout << "p1 的身高: " << *p1.m_Height << endl;
    
    Person p2(p1);      //编译器帮助做了浅拷贝,赋值操作
    cout << "p2 的年龄: " << p2.m_Age << endl;
    cout << "p2 的身高: " << *p2.m_Height << endl;
    //浅拷贝带来的问题是,堆区的内存重复释放。异常
    //解决方式:利用深拷贝
}
​
int main() {
    test01();
​
    system("pause");
    return 0;
}
 
6、初始化列表

#include <iostream>
#include <string>
using namespace std;
​
class Person {
public:
    
    //1.传统初始化操作,构造函数
    /*Person(int a, int b, int c) {
        m_A = a;
        m_B = b;
        m_C = c;
    }*///2.利用初始化列表给参数赋初值
    /*Person() :m_A(10), m_B(20), m_C(30) {     
    }*/
    Person(int a,int b,int c) :m_A(a), m_B(b), m_C(c) {
    }
​
    int m_A;
    int m_B;
    int m_C;
};
​
void test01() {
    //Person p(10, 20, 30);
    //Person  p;
    Person p(30,20,10);
    cout << "m_A = " << p.m_A << endl;
    cout << "m_B = " << p.m_B << endl;
    cout << "m_C = " << p.m_C << endl;
}
​
int main() {
    test01();
​
    system("pause");
    return 0;
}

 

7、类对象作为类成员

#include <iostream>
#include <string>
using namespace std;
​
//手机类
class Phone {
public:
    Phone(string pName) {
        m_PName = pName;
        cout << " Phone 的构造函数调用 " << endl;
    }
​
    ~Phone() {
        cout << " Phone 的析构函数调用 " << endl;
    }
​
    //手机品牌名称
    string m_PName;
};
​
//类对象作为类成员
class Person {
public:
    //Phone m_Phone = pName; 隐式转换法
    Person(string name, string pName) :m_Name(name), m_Phone(pName) {
        cout << " Person 的构造函数调用 " << endl;
    }
​
    ~Person(){
        cout << " Person 的析构函数调用 " << endl;
    }
​
    //姓名
    string m_Name;
    //手机
    Phone m_Phone;
};
​
//当其他类的对象作为本类成员,构造时候先构造类对象,再构造自身
//析构顺序与构造相反
void test01() {
    Person p("张三","苹果MAX");
    cout << p.m_Name << "拿着: " << p.m_Phone.m_PName << endl;
}
​
int main() {
    test01();
​
    system("pause");
    return 0;
}

 

8、静态成员

#include <iostream>
#include <string>
using namespace std;
​
//静态成员函数
//1.所有对象共享同一个函数
//2.静态成员函数只能访问静态成员变量
class Person {
public:
    //静态成员函数
    static void func() {
        m_A = 100;    //静态成员函数可以访问静态成员变量
        
        //静态成员函数不可以访问非静态成员变量
        //m_B = 200; //无法区分到底是那个对象的m_B
        
        cout << "static void func 调用" << endl;
    }
​
    static int m_A;    //静态成员变量(类内声明,类外初始化)
    int m_B;           //非静态成员变量
private:
    //静态成员函数也是有访问权限的
    static void func2() {
        cout << "static void func2 调用" << endl;
    }
};
​
int Person::m_A = 0;   //(类内声明,类外初始化)
//有两种访问方式访问静态成员函数
void test01() {
    //1.通过对象访问
    Person p;
    p.func();
​
    //2.通过类名访问
    Person::func();
    //Person::func2();// 类外访问不到私有静态成员函数
}
​
int main() {
    test01();
​
    system("pause");
    return 0;
}

 

 

posted @ 2021-08-13 11:27  yiwenzhang  阅读(186)  评论(0编辑  收藏  举报