C++类和对象的学习【part2:对象特性】

C++类和对象的学习【part2:对象特性】

构造函数和析构函数

完成对对象的初始化和清理。
编译器强制,自动调用。
构造函数语法:

类名(){}

析构函数语法:

~类名(){}

构造函数的分类及调用

分类方式:

  • 按参数分类:有参构造和无参构造
  • 按类型分类:普通构造和拷贝构造

三种调用方式:

  • 括号法
  • 显示法
  • 隐式转换法

具体程序如下:

// arrayone.cpp -- small arrays of integers
#include <iostream>

using namespace std;

//构造函数的分类和调用
class Person
{
    
    
public: //一定要写在public权限之下
    Person()
    {
    
    
        cout << "无参构造" << endl;
    }
    Person(int a)
    {
    
    
        age = a;
        cout << "有参构造" << endl;
    }
    Person(const Person &p) //记住这种写法先,完成拷贝一份一样的进行构造
    {
    
    
        cout << "拷贝构造" << endl;
        //将传入Person身上的属性,拷贝到当前类上
        age = p.age;
    }

    ~Person()
    {
    
    
        cout << "析构" << endl;
    }

    int age;
};

int main()
{
    
    
    //括号法
    Person p1;     //默认构造函数(无参构造)
    Person p2(10); //有参构造
    Person p3(p2); //拷贝构造
    //注意事项:调用默认构造函数时,不要加(),否则就会认为是函数声明
    cout << "p2's age = " << p2.age << endl;
    cout << "p3's age = " << p3.age << endl;
    //显示法
    Person p4;
    Person p5 = Person(99);
    Person p6 = Person(p5);

    //单独一个这个,是匿名对象,当前行结束,系统立即回收匿名对象
    Person(50);
    //注意事项:不要利用拷贝构造,初始化匿名对象,编译器认为是Person(p3) === Person p3;
    
    //隐式法
    Person p7 = 10; //相当于Person p7 = person(10);(有参构造)
    Person p8 = p7; //拷贝构造
    system("pause");
    return 0;
}

这里展示了不同种类,不同调用方式下的构造函数使用。

拷贝构造函数的调用时机

用法:

  • 使用一个已经构建的对象初始化一个新对象
  • 值传递给函数参数传值
  • 以值方式返回局部对象
// arrayone.cpp -- small arrays of integers
#include <iostream>

using namespace std;

//构造函数的分类和调用
class Person
{
    
    
public:
    Person()
    {
    
    
        cout << "Person的默认构造函数调用" << endl;
    }
    Person(int age)
    {
    
    
        age_ = age;
        cout << "Person的有参构造函数调用" << endl;
    }
    Person(const Person &p)
    {
    
    
        age_ = p.age_;
        cout << "Person的拷贝构造函数调用" << endl;
    }

    ~Person()
    {
    
    
        cout << "Person的析构函数调用" << endl;
    }
    int age_;
};
//使用一个已经构建的对象初始化一个新对象
void test01()
{
    
    
    Person p1(20);
    Person p2(p2);
}
//值传递给函数参数传值
void doit(Person p)
{
    
    
}

void test02()
{
    
    
    Person p;
    doit(p);
}
//以值方式返回局部对象
Person donow()
{
    
    
    Person p1;
    return p1;
}
void test03()
{
    
    
    Person p = donow();
}

int main()
{
    
    
    //test01();
    //test02();
    test03();
    system("pause");
    return 0;
}

这里展示了不同方法下,拷贝构造函数的调用时机(调用方法)。
注意:拷贝构造函数的定义方法是比较特殊的,如下:

Person(const Person &p)
    {
    
    
        age_ = p.age_;
        cout << "Person的拷贝构造函数调用" << endl;
    }

所谓拷贝构造,就是复制了一个新的类,尤其是以值方式返回局部对象,涉及到的就是返回了一个新的对象,这个对象不是p1的本体,若想为p1的本体,应当返回的是引用,即:

return *this;

并且,函数返回类型为引用,即:

Person& (const Person &p)
    {
    
    
        age_ = p.age_;
        cout << "Person的拷贝构造函数调用" << endl;
    }

构造函数调用规则

创建一个类,C++编译器会给每个类都添加至少3个函数。
注意:如果写了有参构造函数,就不再自动默认构造,但依然提供拷贝构造。
这里的拷贝构造可以理解为自动添加了赋值语句,将类A信息拷贝至类B。
注意:如果写了拷贝构造函数,编译器就不再提供其他的普通构造函数了。
这两个注意的意思是要自己写编译器不提供的构造函数(如果要用的话)。

深拷贝和浅拷贝

浅拷贝:简单的赋值
深拷贝:在堆区重新申请内存空间,进行
浅拷贝问题:堆区内存重复释放

初始化列表来初始化属性

这里介绍初始化列表来初始化属性的值,程序如下:

// arrayone.cpp -- small arrays of integers
#include <iostream>

using namespace std;

//构造函数的分类和调用
class Person
{
    
    
public:
    //传统初始化操作
    // Person(int a, int b, int c)
    // {
    
    
    //     A_ = a;
    //     B_ = b;
    //     C_ = c;
    // }
    //通过初始化列表初始化属性
    Person() : A_(10), B_(20), C_(30)//不必注释该行,因为可以利用构造函数重载实现
    {
    
    
    }
    //或者
    Person(int a, int b, int c) : A_(a), B_(b), C_(c)
    {
    
    
    }

    int A_;
    int B_;
    int C_;
};
void test01()
{
    
    
    //Person p(10, 20, 30);
    // Person p;
    Person p(30, 20, 10);
    cout << "A_ = " << p.A_ << endl;
}

int main()
{
    
    
    test01();

    system("pause");
    return 0;
}

这里介绍了三种初始化方法。
其中,传统初始化就是利用有参构造函数,通过传入数值来进行初始化。
进而第二种是列表初始化,但是这种列表只能初始化为固定的值,因为不再赘述。
第三种初始化实质是第一种的升级改造,简化了写法,一目了然,很方便。
这里用到了函数重载,本例中保留了两个构造函数,在调用的时候可以触发函数重载,从而选择合适的构造函数。
(笔者本来以为写错了,准备注释掉上一个构造函数,但发现运行无误,之后baidu发现是出现了函数重载,不得不肃然起敬!)

类对象作为类成员

我们在定义类的时候,可以把其他类作为类成员定义,在以下代码就展示了这一功能,并且加入了初始化列表。

// arrayone.cpp -- small arrays of integers
#include <iostream>

using namespace std;

#include <string>

//类对象作为类成员
class Phone
{
    
    
public:
    Phone(string PName) : PName_(PName)
    {
    
    
        //PName_ = PName;
        cout << "Phone构造函数" << endl;
    }

    string PName_;
};

class Person
{
    
    
public:
    //相当于: Phone phone_ = PName;
    Person(string name, string PName) : name_(name), phone_(PName)
    {
    
    
        cout << "Person构造函数" << endl;
    }

    string name_;
    Phone phone_;
};
//当其他类对象作为本类成员,先构造其他类对象,再构造自身
//析构的顺序正好相反
void test01()
{
    
    
    Person p("张三", "苹果手机");

    cout << p.name_ << "拿着" << p.phone_.PName_ << endl;
}

int main()
{
    
    
    test01();

    system("pause");
    return 0;
}

另外需要注意的是,在应用这一方法时,会先构造其他类对象,再构造自身,而析构顺序正好相反。
这里可以理解为现有零件,才能组装成完整的类,所以是先构造用到的类对象。
程序运行结果如下:
在这里插入图片描述

静态成员

静态成员就是在成员变量和成员函数前加上关键字static,称为静态成员。
静态成员分为:

  • 静态成员变量
    • 所有对象共享同一份数据
    • 在编译阶段分配内存
    • 类内声明,类外初始化
  • 静态成员函数
    • 所有对象共享同一个函数
    • 静态成员函数只能访问静态成员变量

关于静态变量,找到了一篇不错的文章:

https://blog.csdn.net/ichliebecamb/article/details/85097922?utm_medium=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-1.compare&depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-1.compare

猜你喜欢

转载自blog.csdn.net/qq_41883714/article/details/109461896