C++ DAY3

一、类

面向对象的三大特征:封装、继承和多态

C++的类是由C中结构体演变而来,特征中封装就是由类来实现的

结构体:变量和函数

类:成员属性(变量)、成员方法(函数)

类中的成员默认是private

class 类名
{
public:       ---->类外和类内都可以访问
    公有的成员变量/函数
private:      ---->类内可以访问,类外不能访问
    私有的成员变量/函数
protected:
    受保护的成员变量/函数    
};

例如:

1、定义一个矩形类(Rectangle),包含私有成员长(length)、宽(width),定义成员函数:
          设置长度: void set_len(int l);
          设置宽度: void set_wid(int w);
          获取长度: int  get_len();
          获取宽度: int  get_wid();
          显示周长和面积:  void show();


#include 
using namespace std;
 
class Rec
{
    int length;
    int width;
public:
    //所有函数的类内声明
    void set_len(int l);
    void set_wid(int w);
    int get_len();
    int get_wid();
    void show();
 
};
 
void Rec::set_len(int l)
{
    length = l;
}
void Rec::set_wid(int w)
{
    width = w;
}
int Rec::get_len()
{
    return length;
}
int Rec::get_wid()
{
    return width;
}
void Rec::show()
{
    cout << "周长" << 2*(length+width) << endl;
    cout << "面积" << length*width << endl;
}
int main()
{
    Rec r1;    //实例化了一个Rec类的类对象r1
    r1.set_len(10);
    r1.set_wid(3);
    cout << "长:" << r1.get_len() << endl;
    cout << "宽:" << r1.get_wid() << endl;
    r1.show();
    return 0;
}

封装

  1. 把某一事物的所有属性(变量)、行为(成员方法/成员函数),封装成一个整体,给私有的属性,提供公有的接口,以便于用户修改

  2. 类中成员都有访问权限

  3. 访问权限是对整个类而言

  4. 类中同一访问权限可以出现多次,

  5. 类中默认权限是私有权限

二、this指针

this指针是一个特殊的指针,它在每个类的非静态成员函数中都存在。它指向调用该成员函数的对象。换句话说,this指针指向对象本身。

1.格式

类名 *const this;  ----->不能修改指针指向

2.必须使用this指针的场合

        i.成员函数的参数和类中成员属性同名时,需要使用this指向表明哪一个是类中的成员属性

        ii.拷贝赋值函数中,需要返回自身的引用,也需要使用this指针(后面讲)

#include 
using namespace std;
 
class Rec
{
    int length;
    int width;
public:
    //所有函数的类内声明
    void set_len(int length);
    void set_wid(int width);
    int get_len();
    int get_wid();
    void show();
};
 
void Rec::set_len(int length)
{
    this->length = length;
}
 
void Rec::set_wid(int width)
{
    this->width = width;
}
 
int Rec::get_len()
{
    return length;
}
 
int Rec::get_wid()
{
    return width;
}
 
void Rec::show()
{
    cout << "周长" << 2*(length+width) << endl;
    cout << "面积" << length*width << endl;
}
int main()
{
    Rec r1;    //实例化了一个Rec类的类对象r1
    Rec r2;
    r2.set_len(20);
 
    r1.set_len(10);
    r1.set_wid(3);
    cout << "长:" << r1.get_len() << endl;
    cout << "宽:" << r1.get_wid() << endl;
    r1.show();
    return 0;
}

三、类中的特殊成员函数

如果程序员不手动写出来,系统会默认提供

共6个成员函数:构造函数、析构函数、拷贝构造函数、拷贝赋值函数,取地址运算符的重载和常取地址运算符的重载

1.构造函数

用于实例化类对象,系统会自动调用,自动提供的是无参构造

如果手动定义构造函数,系统不会再提供一个无参构造

i.功能

实例化类对象,申请空间

ii.格式

类名(参数)
{
    函数体;
}

iii.构造函数的调用时机

1. 栈区:

  • 什么时候实例化类对象,什么时候调用
    • 当你在栈区创建(实例化)一个类的对象时,相应的构造函数会立即被调用。
    • 例如:Stu s1; 在这里,Stu类的无参构造函数会被立即调用。

2. 堆区:

  • 定义类的指针时,不调用构造函数
    • 当你仅仅定义一个指向类的指针时,不会调用任何构造函数,因为此时还没有为对象分配内存。
    • 例如:Stu* p1; 在这里,我们只是定义了一个指针,没有创建对象,所以没有构造函数被调用。
  • 什么时候new,什么时候调用

    • 当你使用new关键字在堆区创建一个类的对象时,相应的构造函数会被调用。
    • 例如:p1 = new Stu(); 在这里,我们在堆区为Stu对象分配了内存,并调用了其无参构造函数。

总结:

  • 在栈区,当你创建一个对象时,构造函数会立即被调用。
  • 在堆区,仅当你使用new关键字创建对象时,构造函数才会被调用。仅仅定义一个指针并不会调用构造函数。

iv.构造函数允许函数重载

提供一个无参构造函数

可以显性定义出有参构造,但此时系统不会再提供无参构造,可自己再去定义显性的无参构造

构造函数时允许函数重载的

#include 
using namespace std;
 
class Stu
{
    string name;
    int age;
public:
    //定义Stu类的无参构造
    Stu()
    {
        cout << "Stu的无参构造" << endl;
    }
    //函数重载
    Stu(string name,int age)
    {
        this->name = name;
        this->age = age;
        cout << "Stu的有参构造" << endl;
    }
    Stu(string name)
    {
        this->name = name;
        cout << "Stu的对name的有参构造" << endl;
    }
};
 
int main()
{     
    Stu s1("zhangsan",18);  //栈区的类对象
    Stu *p;  //在栈区申请一个Stu*类型的指针
    p = new Stu("lisi");    //在堆区申请一个Stu的空间,会调用构造函数
    return 0;
}

v.构造函数的初始化列表

构造函数提供的初始化列表机制,可在函数体外执行初始化操作

格式:类型(形参):成员属性1(形参1),成员属性2(形参2),······{函数体内容}

由:引出                成员之间以,分隔

必须使用初始化列表的情况

形参和成员属性同名

类中有引用成员时,需要使用初始化列表(引用得有对象)

类中有const修饰的对象

一个类中有另一个类的子对象,需要使用初始化列表(如果两个类都有无参构造,是不需要写初始化列表)

C++ DAY3_第1张图片

 包含的Per先构造,Stu后构造

示例:

//包含其他类的子对象
#include 
using namespace std;
class Per
{
    string name;
public:
    //Per提供的有参构造
    Per(string name)
    {
        this->name = name;
        cout << "Per的有参构造" << endl;
    }
    Per()
    {
        cout << "Per的无参构造" << endl;
    }
    //一个show函数,输出name
    void show()
    {
        cout << "Per中的name=" << name << endl;
    }
};
 
class Stu
{
    int score;
public:
    Per p1;    //Stu类中,包含另一个类的子对象,且另一个类只有有参构造函数
public:
    Stu(string name):p1(name)    //必须使用初始化列表,并且在初始化列表显性调用另一个类的构造函数
    {
        cout << "Stu的有参构造" << endl;
    }
    Stu()
    {
        cout << "Stu的无参构造" << endl;
    }
};
 
int main()
{
    Stu s1("zhangsan");
    //通过s1这个类对象,调用Per中的show函数
    s1.p1.show();
 
    Stu s2;
    return 0;
}
 
//同名或者是包含引用成员/const修饰的成员
class Stu
{
    string name;
    //int &age;     //类中有引用成员
    const int score;
public:
    //定义Stu类的无参构造
//    Stu()
//    {
//        cout << "Stu的无参构造" << endl;
//    }
    //函数重载,使用初始化列表的方式
    //初始化列表也可以解决形参和成员属性同名问题
    Stu(string name,int age):name(name),score(age)  //在函数体外给成员属性初始化
    {
        cout << "Stu的有参构造" << endl;
    }
    void show()
    {
        cout << name << " " << score << endl;
    }
};
 
int main()
{     
    Stu s1("zhangsan",18);  //栈区的类对象
    s1.show();
    Stu *p;  //在栈区申请一个Stu*类型的指针
    p = new Stu("lisi",20);    //在堆区申请一个Stu的空间,会调用构造函数
    return 0;
}

2.析构函数

i.功能

用于回收类对象的空间

public权限 不需要参数  不支持重载

ii.格式

~类名()
{

}

iii.调用时机

1.栈区:

栈区:类对象消亡时,自动调用

2.堆区:

堆区:什么时候delete,什么时候调用析构函数

先构造的后解析,后构造的先解析!!!

iv.需要显性定义出析构函数的情况

当类中指针指向堆区空间,需要使用析构函数将其释放,若类对象消亡后找不到指针指向堆区空间,堆区空间未释放,会造成内存泄漏

#include 
using namespace std;
 
class Stu
{
    int *p;
public:
    Stu():p(new int)    //在不传参的情况下,给指针成员p用堆区的空间初始化
    {
        cout << "Stu无参构造" << endl;
    }
 
    Stu(int a):p(new int(a))   //在参数为整形变量的情况下,让p指向堆区申请的空间
    //但是把这篇空间的内容,用传过来的整形变量初始化
    {
        cout << "Stu的有参构造" << endl;
    }
 
    Stu(int *p)   //要求传过来的指针的值,一定是指向堆区空间
    {
        this->p = p;
        cout << "Stu传指针的有参构造" << endl;
    }
 
    ~Stu()
    {
        //在析构之前释放堆区的空间
        cout << "准备释放空间:" << p << endl;
        delete p;
        p = nullptr;
        cout << "Stu的析构函数" << endl;
    }
};
 
int main()
{
    Stu s1(90);
    return 0;
}

3.拷贝构造函数

i.功能

使用已有的类对象,给类对象初始化的时候,自动调用拷贝构造函数

ii.格式

类名(类名 &other)
{
    函数体的内容;
}
 
拷贝构造函数,是一个构造函数,
函数名:类名
形参:其他类对象的引用

iii.调用时机

1.使用已有的类对象给新的类对象初始化

2.当函数的形参是一个类对象时,也会调用拷贝构造函数

3.当函数的返回值是一个类对象时,也会调用拷贝构造函数

#include 
using namespace std;
class Stu
{
    string name;
public:
    Stu()
    {
        cout << "Stu的无参构造" << endl;
    }
    Stu(string name)
    {
        this->name = name;
        cout << "Stu的有参构造" << endl;
    }
    Stu(Stu &other)   //Stu的拷贝构造函数,和无参构造以及有参构造构成函数冲澡
    {
        this->name = other.name;
        cout << "Stu的拷贝构造函数" << endl;
    }
 
    void show()
    {
        cout << name << endl;
    }
};
Stu fun(Stu s1)  //定义了一个函数fun,形参是一个类对象
{
    cout << "调用成功" << endl;
    return s1;
}
int main()
{
    Stu s1("zhangsan");
    cout << "s1:";
    s1.show();
    Stu s2 = s1;   //拷贝构造函数
    cout << "s2:";
    s2.show();
    fun(s2);
    return 0;
}

iv.深浅拷贝问题

在类中存在指针时会发生

                               C++ DAY3_第2张图片C++ DAY3_第3张图片

#include 
using namespace std;
 
class Stu
{
    int *p;
public:
    Stu():p(new int)    //在不传参的情况下,给指针成员p用堆区的空间初始化
    {
        cout << "Stu无参构造" << endl;
    }
 
    Stu(int a):p(new int(a))   //在参数为整形变量的情况下,让p指向堆区申请的空间
    //但是把这篇空间的内容,用传过来的整形变量初始化
    {
        cout << "Stu的有参构造" << endl;
    }
    Stu(int *p)   //要求传过来的指针的值,一定是指向堆区空间
    {
        this->p = p;
        cout << "Stu传指针的有参构造" << endl;
    }
    ~Stu()
    {
        //在析构之前释放堆区的空间
        cout << "准备释放空间:" << p << endl;
        delete p;
        p = nullptr;
        cout << "Stu的析构函数" << endl;
    }
    void show()
    {
        cout << "p=" << p << endl;
    }
    void set_p(int a)
    {
        *p = a;    //通过指针变量p,修改内存中的值
    }
    void show_p()
    {
        cout << *p << endl;  //通过指针变量,输出p指向的内存中的值
    }
    
    //拷贝构造函数
    Stu(Stu &other)
    {
        p = new int;  //手动让s2的指针p指向堆区的空间
        //实现深拷贝
        *p = *(other.p);
        cout << "Stu的拷贝构造函数" << endl;
    }
};
int main()
{
    Stu s1(90);    //会给s1的指针成员在堆区申请一片空间使用90初始化
    Stu s2 = s1;   //申请了一个Stu的类对象的空间,也实现了用s1的值初始化s2
    //左调右参
    //上面一行,调用了拷贝构造函数
    //使用了默认提供的拷贝构造,会造成指针成员,指向同一片空间的问题
//    cout << "s1:";
//    s1.show();
//    cout << "s2:";
//    s2.show();
    s1.show_p();
    s2.show_p();
    s1.set_p(101);
    s1.show_p();
    s2.show_p();
 
    return 0;
}

设计一个Per类,类中包含私有成员:姓名、年龄、指针成员身高、体重,再设计一个Stu类,类中包含私有成员:成绩、Per类对象 p1,设计这两个类的构造函数、析构函数和拷贝构造函数。

#include 
using namespace std;

class Per {
private:
    string name;
    int age;
    int *height;
    int *weight;

public:
    // 默认构造函数
    Per() : name(""), age(0), height(new int(0)), weight(new int(0)) {
        cout << "Per default constructor called." << endl;
    }

    // 带参数的构造函数
    Per(string n, int a, int h, int w) 
        : name(n), age(a), height(new int(h)), weight(new int(w)) {
        cout << "Per parameterized constructor called." << endl;
    }

    // 拷贝构造函数
    Per(const Per& p) {
        name = p.name;
        age = p.age;
        height = new int(*(p.height));
        weight = new int(*(p.weight));
        cout << "Per copy constructor called." << endl;
    }

    // 析构函数
    ~Per() {
        delete height;
        delete weight;
        cout << "Per destructor called." << endl;
    }

    // 显示信息的函数
    void display() const {
        cout << "Name: " << name << ", Age: " << age 
             << ", Height: " << *height << ", Weight: " << *weight << endl;
    }
};

class Stu {
private:
    int score;
    Per p1;

public:
    // 默认构造函数
    Stu() : score(0), p1() {
        cout << "Stu default constructor called." << endl;
    }

    // 带参数的构造函数
    Stu(int s, const Per& p) : score(s), p1(p) {
        cout << "Stu parameterized constructor called." << endl;
    }

    // 拷贝构造函数
    Stu(const Stu& s) {
        score = s.score;
        p1 = s.p1;  // 使用Per类的拷贝构造函数
        cout << "Stu copy constructor called." << endl;
    }

    // 析构函数
    ~Stu() {
        cout << "Stu destructor called." << endl;
    }

    // 显示信息的函数
    void display() const {
        cout << "Score: " << score << ", ";
        p1.display();
    }
};

int main() {
    Per person("John", 25, 175, 70);
    Stu student(90, person);

    Stu anotherStudent = student;  // 使用拷贝构造函数

    cout << "Person details:" << endl;
    person.display();

    cout << "Student details:" << endl;
    student.display();

    cout << "Another student details:" << endl;
    anotherStudent.display();

    return 0;
}

你可能感兴趣的:(c++,开发语言)