【C++】类与对象基本知识 (构造 析构 拷贝 explicit 对象数组 动态静态对象)

目录

1.类与对象基本概念

2.构造函数

3.析构函数

4.构造和析构函数调用顺序

5.拷贝构造函数

6.浅拷贝和深拷贝

7.初始化列表

8.explicit防止构造函数隐式转换

9.对象数组

10.动态对象

10.1 动态对象创建

10.2 动态对象数组

11.静态成员

11.1 静态成员变量

11.2 静态成员函数


this指针,友元,重载运算符等后续单独出。

1.类与对象基本概念

  • 类的封装性:将具有共性的数据和方法封装在一起,加以权限区分,限制用户的访问。

  • 类的权限:

    private(私有),protected(保护),public(共有)

    类的内部(class内)没有权限限制,权限限制在类的外部实现。

    默认为私有的。
    private成员:只能被本类成员函数或友元访问。
    public成员:在任何地方都可以访问。
    protected成员:在private的基础上,还可以被派生类访问。

    加了作用域后也相当于在类内。

2.构造函数

  • 定义:

    • 构造器 constructor

      对类创建的对象进行初始化 特点: 1.名称与类名一致;

      2.没有返回类型;

      3.经常被重载;

      4.每次创建对象都会自动调用构造函数。

    • 默认构造器:default constructor 可以进行无参调用的构造函数

      特点: 1.一个类没有定义构造函数时,c++编译器会自动生成默认构造函数; 2.类中只要有定义了构造函数,c++编译器就不会生成默认构造函数; 3.所有参数都是默认参数的普通构造函数,也可以充当默认构造函数。

    • 调用构造函数各种方式:

    class Data
    {
    public:
    int mA;
    public:
    //无参构造函数
    Data()
    {
    mA=0;
    cout<<"无参构造函数"<<endl;
    }
    //有参构造函数
    Data(int a)
    {
    mA=a;
    cout<<"有参构造函数 mA="<<mA<<endl;
    }
    };
    int main()
    {
    //隐式调用无参构造函数(推荐)
    Data ob1;
    //显示调用无参构造函数
    Data ob2 = Data();
    //隐式调用有参构造函数(推荐)
    Data ob3(10);
    //显示调用有参构造函数
    Data ob4 = Data(10);
    //匿名对象(无参) 当前语句技术 立即释放
    Data();
    Data(20);
    //构造函数隐式转换(类中只有一个数据成员)
    Data ob5 = 100; //相当于Data ob5(100)
    }
  • 注:自己写时,有参无参的构造函数都要实现。

3.析构函数

  • 定义

    析构器 destructor

    当对象不再使用时(出了所在作用域),对该对象进行清理工作 特点:(与构造器相反) 1.名称为“~类名”

    2.没有返回类型;

    3.没有参数;

    4.每次析构对象都会自动调用析构函数。

    5.析构函数理论上可写可不写,但有指针的时候必须写,不然指针开辟的空间无法删除。

4.构造和析构函数调用顺序

#include<iostream>
using namespace std;
​
//创建一个类
class Data
{
private:
     int a;
public:
     Data();
     Data(int b);
     ~Data();
};
//默认构造函数
Data::Data()
{
     a = 999;
     cout << "调用默认构造函数:" << a << endl;
}
//构造函数
Data::Data(int b)
{
     a = b;
     cout << "调用构造函数:" << a << endl;
}
​
Data::~Data()
{
     cout << "调用析构函数,a="<<a << endl;
}
​
int main()
{
     Data D0;
     Data D1(10);
     Data D2(20);
     {
          Data D3(30);
     }
     Data D4(40);
     return 0;
}

调用顺序:
1.D0默认构造函数。
2.D1构造函数。
3.D2构造函数。
4.D3构造函数。
5.此时遇到},自动对{}里的内容调用析构函数。
6.D4构造函数。
7.遇到},从下往上依次对D4,D2,D1,D0调用析构函数。

总结:1.不加参时自动调用默认构造函数;2.遇到}自动调用析构函数;3.析构顺序为从下往上(最先构造的最后被析构).

5.拷贝构造函数

  • 定义:

    拷贝构造函数 copy constructor 用一个已经存在的对象创建一个新的对象

    特点:

    1.如果没有实现拷贝构造函数,编译器自动生成一个拷贝构造函数(行为是bit-wise copy) 2.自定义拷贝构造函数:当编译器提供的拷贝构造函数无法满足需求。(如年龄大两岁)

  • 什么时候调用?

    新对象被旧对象初始化时。

  • 拷贝构造 和 无参构造 有参构造的关系

    如果用户定义了 拷贝构造或者有参构造 都会屏蔽无参构造。

    如果用户定义了 无参构造或者有参构造 不会屏蔽拷贝构造。

  • 拷贝构造几种调用形式

    1、旧对象给新对象初始化 调用拷贝构造。

    2、给对象取别名 不会调用拷贝构造。

    3、普通对象作为函数参数,调用函数时 会发生拷贝构造。

    4、函数返回值普通对象。

    Visual Studio会发生拷贝构造

    #include<iostream>
    using namespace std;
    ​
    //创建一个类
    class Data
    {
    public:
         int a;
    public:
         Data();
         Data(int b);
         ~Data();
         Data(const Data& D);
    };
    //默认构造函数
    Data::Data()
    {
         a = 999;
         cout << "调用默认构造函数:" << a << endl;
    }
    //构造函数
    Data::Data(int b)
    {
         a = b;
         cout << "调用构造函数:" << a << endl;
    }
    //析构函数
    Data::~Data()
    {
         cout << "调用析构函数,a="<<a << endl;
    }
    //拷贝构造函数
    Data::Data(const Data& D)//D是旧对象的数据,函数内调用的是新对象数据
    {
         //用旧对象的数值给新对象赋值
         a = D.a;
         cout << "拷贝构造函数" << endl;
    }
    ​
    int main()
    {
         Data D1(10); //调用构造函数
         Data D2=D1; //调用拷贝构造函数
         Data& DD = D1; //取别名不会调用拷贝构造函数
         cout << "D1:" << D1.a << endl;
         cout << "D2:" << D2.a << endl;
         return 0;
    }

    运行结果:

  •  

6.浅拷贝和深拷贝

  • 什么是浅拷贝和深拷贝?

    • 浅拷贝:只是数值一样。如果成员是指针,则原函数和拷贝构造函数公用一个地址。

    • 深拷贝:为拷贝函数的指针成员开辟新地址。

  • 分别在什么时候用浅拷贝和深拷贝?

    • 默认的拷贝构造 都是浅拷贝。

    • 如果类中没有指针成员, 不用实现拷贝构造和析构函数。

    • 如果类中有指针成员,且指向堆区空间, 必须实现析构函数释放指针成员指向的堆区空间,必须实现拷贝构造完成深拷贝动作(否则指针区域会被析构多次)。

7.初始化列表

  • 对象成员

    定义:类中的成员也可以是对象,叫做对象成员。

    当一个类中有对象成员,无参调用时,不需要初始化列表。

    #include<iostream>
    using namespace std;
    class A
    {
    public:
        int mA;
    public:
        A()
        {
            mA = 0;
            cout << "A类无参构造" << endl;
        }
        A(int a)
        {
            mA = a;
            cout << "A的有参构造" << endl;
        }
        ~A()
        {
            cout << "A的析构函数" << endl;
        }
    };
    class B
    {
    public:
        int mB;
        A ob;//对象成员
    public:
        B()
        {
            cout << "B类的无参构造" << endl;
        }
        ~B()
        {
            cout << "B的析构函数" << endl;
        }
    };
    int main(int argc, char* argv[])
    {
        B ob1;
        return 0;
    }

    调用顺序:

  •  

  • 调用有参构造函数时需要初始化列表

    #include<iostream>
    using namespace std;
    class A
    {
    public:
        int mA;
    public:
        A()
        {
            mA = 0;
            cout << "A类无参构造" << endl;
        }
        A(int a)
        {
            mA = a;
            cout << "A的有参构造" << endl;
        }
        ~A()
        {
            cout << "A的析构函数" << endl;
        }
    };
    class B
    {
    public:
        int mB;
        A ob;//对象成员
    public:
        B()
        {
            cout << "B类的无参构造" << endl;
        }
        //ob(a)隐式调用有参构造
        //
        B(int a, int b):ob(a)
        {
            mB = b;
            cout << "B类的有参构造" << endl;
        }
        ~B()
        {
            cout << "B的析构函数" << endl;
        }
    };
    int main(int argc, char* argv[])
    {
        B ob1(10, 20);
        cout << "mA =" << ob1.ob.mA << ", mB =" << ob1.mB << endl;
        return 0;
    }

    调用顺序:

     

8.explicit防止构造函数隐式转换

explicit:声明为explicit的构造函数不能在隐式转换中使用

//无explicit
#include<iostream>
using namespace std;
class MyString {
public:
     MyString(int n) {
          cout << "MyString int" << endl;
     }
     MyString(const char* s) {
          cout << "MyString char" << endl;
     }
};
int main()
{
     MyString str1 = 1;//这样赋值容易产生歧义,是数值赋值?还是类初始化?
     MyString str2(1);//正常调用
     return 0;
}
#include<iostream>
using namespace std;
class MyString {
public:
     explicit MyString(int n) {
          cout << "MyString int" << endl;
     }
     MyString(const char* s) {
          cout << "MyString char" << endl;
     }
};
int main()
{
     //如果有explicit,则无法这样赋值
     //MyString str1 = 1;
     MyString str2(1);//正常调用
     return 0;
}

9.对象数组

本质是数组,数组的每个元素是对象。

#include<iostream>
using namespace std;
class A {
public:
     int ma;
     A() {
          ma = 0;
          cout << "A():" << ma << endl;
     }
     A(int a) {
          ma = a;
          cout << "A(int a):" << ma << endl;
     }
     ~A()
     {
          cout << "~A:" << ma << endl;
     }
};
int main()
{
     //对象数组每个元素都会调用构造函数和析构函数
     //对象数组不初始化,每个元素调用无参构造
     A arr1[5];
​
     //对象元素初始化,必须调用显示有参构造,逐个初始化
     A arr2[3]={A(10),A(20),A(30)};//必须用{}
     cout <<"sizeof(arr2):"<< sizeof(arr2) << " " <<"sizeof(arr2[0]):" << sizeof(arr2[0]) << endl;
     int n = sizeof(arr2) / sizeof(arr2[0]);
     for (int i = 0; i < n; i++)
     {
          cout << arr2[i].ma << " ";
     }
     cout<< endl;
     return 0;
}

 

10.动态对象

10.1 动态对象创建

好处:可根据需要分配空间。

具体操作:不用malloc,而用new创建动态对象,用delete释放动态对象。

malloc的问题:
1.必须直到对象长度。
2.需要强制转换返回值。
3.可能内存分配失败。
4.构造函数中,不能自动调用初始化函数。
new流程:
自动分配内存+初始化。
deletel:
自动调用析构函数+释放内存。
#include<iostream>
using namespace std;
class Person {
public:
     char* name;
     int age;
public:
     //无参构造
     Person() {
          cout << "Person()" << endl;
          name = new char[sizeof("#")+1];
          strcpy(name, "#");
          age = 0;
     }
     //有参构造
     Person(const char* na, int ag) {
          cout << "Person(char,int)" << endl;
          name = new char[strlen(na) + 1];
          strcpy(name, na);
          age = ag;
     }
     void print() {
          cout << "name:" << name << " " << "age:" << age << endl;
     }
     ~Person() {
          cout << "~Person" << endl;
          if (name != NULL) {
               delete[]name;
               name = NULL;
          }
     }
};
int main()
{
     Person *p1 = new Person;
     Person *p2 = new Person("YY~", 18);
     p1->print();
     p2->print();
     delete p1;
     delete p2;
     return 0;
}

 

10.2 动态对象数组

对象数组中每个对象都要调用构造函数。

除了栈上可以聚合初始化,其他情况必须提供一个默认的构造函数。

#include<iostream>
using namespace std;
class Person {
public:
     char* name;
     int age;
public:
     Person() {
          cout << "Person()" << endl;
          name = NULL;
          age = 0;
     }
     Person(const char* na, int ag) {
          cout << "Person(char,int)" << endl;
          name = new char[strlen(na) + 1];
          strcpy(name, na);
          age = ag;
     }
     ~Person() {
          cout << "~Person()" << endl;
          if (name != NULL) {
               delete[]name;
          }
     }
};
//栈聚合
void stack() {
     //栈聚合初始化
     Person person[] = { Person("Y",19),Person("ZZYY",99) };
     for (int i = 0; i < 2; i++)
     {
          cout << person[i].name << " " << person[i].age << endl;
     }
​
     //创建堆上对象数组必须提供构造函数
     Person* all = new Person[5];
     delete[]all;
}
int main()
{
     stack();
     return 0;
}

11.静态成员

不管类有多少对象,静态成员只有一个拷贝(副本),且这个拷贝 被类中所有对象共享。

11.1 静态成员变量

static修饰的静态成员 属于类,而不是具体的对象。

所有对象共享同一个静态成员。

  • 注意!!!!

    static修饰的成员 定义类的时候 必须分配空间。

    static修饰的静态成员数据 必须类中定义 并且 类外初始化。

    #include<iostream>
    using namespace std;
    class Data {
    public:
         int a;//普通成员
         //类中定义!
         static int b;//静态数据
    };
    ​
    //类外初始化!
    int Data::b = 99;
    ​
    void test()
    {
         //静态成员两种访问方式:
         //1.静态成员 可以通过类名称直接访问(属于类)
         cout <<" Data::b :"<< Data::b << endl;
         //2.静态成员 也可通过对象访问(共享)
         Data ob1;
         cout << "ob1.b :"<<ob1.b << endl;
    ​
         //所有对象共享一个静态成员
         ob1.b = 222;
         Data ob2;
         ob2.b = 333;
         cout <<"ob1.b = 222; AND ob2.b = 333;  b=" <<Data::b << endl;
    }
    int main()
    {
         test();
         return 0;
    }

     

11.2 静态成员函数

静态成员函数 属于类 而不是对象(所有对象 共享)

  • 定义:

    class Data{
         static void func(){
              
         }
    }
  • 注!!!!

    静态成员函数可直接通过类名访问。

    静态函数内 只能操作 静态对象。

    class Data{
    private:
         int data;
         static int a;
    public:
         static int getA()
         {
              data=10;//error 非静态对象,不能在静态数组里操作
              return a;
         }
    }
    int Data::a=99;//静态成员类外定义
    void test(){
         cout<<Data::getA()<<endl;//静态成员函数可直接通过类名访问。
    }

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值