C++类和对象2

简介: C++类和对象

C++类和对象1:https://developer.aliyun.com/article/1548154

4、构造函数调用规则

默认情况下,C++编译器至少给一个类添加3个函数

a、默认构造函数(无参、函数体为空);b、默认析构函数(无参、函数体为空);c、默认拷贝构造函数,对属性进行值拷贝;d、如果用户定义了有参构造函数,编译器会提供拷贝构造函数,不会提供无参构造函数;如果用户定义了拷贝构造函数,编译器不会提供无参构造函数。

#include <iostream>
 
using namespace std;
 
//构造函数的调用规则
//1、创建一个类,C++编译器会给每个类都添加至少3个函数;
//默认构造 (空实现)
//析构函数 (空实现)
//拷贝构造 (值拷贝)
class Person {
public:
    Person() {
        cout << "默认构造函数" << endl;
    }
 
    Person(int age) {
        cout << "有参构造函数" << endl;
        m_Age = age;
    }
//
//    Person(const Person &p) {
//        m_Age = p.m_Age;
//        cout << "拷贝构造函数" << endl;
//    }
 
    ~Person() {
        cout << "析构函数" << endl;
    }
 
    int m_Age;
};
 
void test01() {
    Person p;
    p.m_Age = 18;
    Person p2(p);
    cout << "p2的年龄为:" << p2.m_Age << endl;
}
 
int main() {
    test01();
    return 0;
 
 
}
 
 
默认构造函数
p2的年龄为:18
析构函数
析构函数

5、深拷贝与浅拷贝

浅拷贝:简单的赋值拷贝操作

深拷贝:在堆区重新申请空间,进行拷贝操作

浅拷贝,堆区的内存重复释放

#include <iostream>
 
using namespace std;
 
//深拷贝与浅拷贝
class Person {
public:
    Person() {
        cout << "Person的默认构造函数调用" << endl;
    }
 
    Person(int age, int hegith) {
        m_Age = age;
        m_Height = new int(hegith);//堆区
        cout << "Person的有参构造函数调用;" << endl;
    }
 
    Person(const Person &p) {
        cout << "执行拷贝构造函数" << 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 << "析构函数" << endl;
    }
 
    int m_Age;//年龄
    int *m_Height;//身高
};
 
void test01() {
    Person p1(18, 160);
    cout << "p1的年龄为:" << p1.m_Age << " " << *p1.m_Height << endl;
    Person p2(p1);
    cout << "p2的年龄为:" << p2.m_Age << " " << *p2.m_Height << endl;
}
 
int main() {
    test01();
    return 0;
 
 
}
 
 

6 、初始化列表

作用:C++提供了初始化列表语法,用来初始化属性;

#include <iostream>
 
using namespace std;
 
class Person {
public:
//    初始化
//    Person(int a, int b, int c) {
//        m_A = a;
//        m_B = b;
//        m_C = c;
//    }
 
//初始化列表初始化属性
    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 p1(1, 2, 3);
    Person p1(1,2,3);
    cout << p1.m_A << p1.m_B << p1.m_C << endl;
}
 
int main() {
//    初始化列表
    test01();
 
    return 0;
 
 
}
 
 

7、类对象作为类成员

当类中成员是其他类对象时,我们称该成员为对象成员,

构造顺序:先调用对象成员的构造,再调用本类构造,析构顺序相反.

#include <iostream>
 
using namespace std;
 
class Phone {
public:
    Phone() {
        cout << "Phone 的构造函数" << endl;
    }
 
    Phone(string name) : p_name(name) {
        cout << "Phone 有参的构造函数" << endl;
    }
 
    ~Phone() {
        cout << "Phone 的析构函数" << endl;
    };
    string p_name;
};
 
class Person {
public:
    Person() {
        cout << "Person 的构造函数" << endl;
    }
 
    Person(string name, string pName) : name(name), phone(pName) {
        cout << "Person 的有参构造函数" << endl;
    }
 
    ~Person() {
        cout << "Person 的析构函数" << endl;
    };
    string name;
//    对象成员
    Phone phone;
};
//当类中成员是其他类对象时,我们称该成员为对象成员,构造顺序:先调用对象成员的构造,再调用本类构造,析构顺序相反.
void test() {
    Person person("zhangsan","sanxing");
    cout<<person.phone.p_name<<endl;
}
 
int main() {
//    初始化列表
    test();
    return 0;
 
 
}
 
 
Phone 有参的构造函数
Person 的有参构造函数
sanxing
Person 的析构函数
Phone 的析构函数

8 、静态成员

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

静态成员变量:所有对象共享同一份数据,在编译阶段分配内存,类内申明,类外初始化;

静态成员函数:所有对象共享同一个函数,静态成员函数只能访问静态成员变量。

#include <iostream>
 
using namespace std;
 
//静态成员就是在成员变量和成员函数前加上关键字static,成为静态成员。
 
 
 
//静态成员变量
class Person {
public:
    //静态成员变量:所有对象共享同一份数据,在编译阶段分配内存,类内申明,类外初始化;
    static int m_A;
    int m_B;
 
//静态成员函数:所有对象共享同一个函数,静态成员函数只能访问静态成员变量。
    static void func() {
 
        cout << "static void func调用" << endl;
//        静态成员函数只能访问静态成员变量
        cout << m_A << endl;
    }
};
 
int Person::m_A = 100;
 
void test() {
    Person p;
    cout << p.m_A << endl;
    Person p2;
    p2.m_A = 200;
    cout << p.m_A << endl;
 
}
 
//静态成员变量
void test02() {
    Person p1;
//    1、通过对象进行访问
    cout << p1.m_A << endl;
//   2、通过类访问
    cout << Person::m_A << endl;
}
void test03(){
    Person::func();
}
int main() {
//    test();
//    test02();
    test03();
    return 0;
 
 
}
 
 
static void func调用
100

三、对象模型和this指针

1、成员变量与成员函数分开存储

空对象占用1个字节空间,非静态成员属于类的对象。

#include <iostream>
 
using namespace std;
 
//成员变量和成员函数分开存储
class Person {
public:
    int m_A; //非静态成员变量,属于类的对象
    static int m_B; //静态成员变量,不属于类的对象
    void func() {};//非静态成员函数,不属于类的对象
 
    static void funcs() {}; //静态成员函数,不属于类的对象
};
 
int Person::m_B = 100;
 
void test01() {
    Person p;
    //空对象占用内存空间为:1
    //C++编译器会给每个空对象也分配一个字节空间,是为了区分空对象占内存的位置
//    每个空对象都有独一无二的内存地址
    cout << "size of p=" << sizeof(p) << endl;
}
 
void test02() {
    Person p;
    cout << "size of p=" << sizeof(p) << endl;
}
 
int main() {
//    test01();
    test02();
    return 0;
 
 
}
 
 
size of p=4

2、this指针概念

this指针指向被调用的成员函数所属的对象。

this指针是隐含每一个非静态成员函数内的一种指针;

this指针不需要定义,直接使用即可;

用途:

当形参和成员变量同名时,可用this指针来区分;

在类的非静态成员函数中返回对象本身,可使用return *this;

#include <iostream>
 
using namespace std;
 
class Person {
public:
//    Person(int age) : age(age) {};
    Person(int age) {
//    this指针指向被调用的成员函数所属的对象。
        this->age = age;
    }
 
    Person &PersonAddAge(Person &p) {
        this->age += p.age;
        return *this;
    }
 
    int age;
};
 
//1 解决名称冲突
void test01() {
    Person p(18);
    cout << "年龄:" << p.age << endl;
}
 
//2 返回对象本身用*this
void test02() {
    Person p1(10);
    Person p2(20);
    cout << "年龄:" << p2.age << endl;
//    链式编程
    p2.PersonAddAge(p1).PersonAddAge(p1).PersonAddAge(p1).PersonAddAge(p1);
    cout << "年龄:" << p2.age << endl;
}
 
int main() {
//    test01();
    test02();
    return 0;
}
 
 


年龄:20
年龄:60

3、空指针访问成员函数

C++中空指针也是可以调用成员函数的,但是也要注意有没有用到this指针;

如果用到this指针,需要加以判断保证代码的健壮性;

#include <iostream>
 
using namespace std;
 
//C++中空指针也是可以调用成员函数的,但是也要注意有没有用到this指针;
//如果用到this指针,需要加以判断保证代码的健壮性;
class Person {
public:
    void showClassName() {
        cout << "this is Person class" << endl;
    }
 
    void showPersonAge() {
//        防止空指针访问,导致程序崩溃
        if (this == NULL) {
            return;
        }
        cout << "年龄:" << this->m_Age << endl;
 
 
    }
 
    int m_Age;
};
 
void test01() {
    Person *p = NULL;
    p->showClassName();
    p->showPersonAge();
}
 
int main() {
    test01();
    return 0;
}
 
 
this is Person class

4、const修饰成员函数

常函数:成员函数后加const后我们称这个函数为常函数,常函数内不可以修改成员属性,成员属性声明时加关键字mutable后,在常函数中依然可以修改。

常对象:申明对象前加上const称该对象为常对象,常对象只能调用常函数。

#include <iostream>
 
using namespace std;
 
//常函数:成员函数后加const后我们称这个函数为常函数,常函数内不可以修改成员属性,成员属性声明时加关键字mutable后,在常函数中依然可以修改。
class Person {
public:
//    this指针的本质是指针常量,指针的指向是不可以修改的
//const Person * const this; 成员函数后边的const,本质是修改this指正
    void showPerson() const {
        cout << this->m_A << endl;
//        m_A=100; //常函数无法修改非mutable修饰的变量
        this->m_B = 1000;
    }
 
    void showNum() {
        cout << m_B << endl;
    }
 
    int m_A;
    mutable int m_B; //常函数中可以修改
};
 
//常对象:申明对象前加上const称该对象为常对象,常对象只能调用常函数。
void test01() {
//    常对象
    const Person p1{};
    p1.m_B = 200;
//    常对象只能调用常函数
    p1.showPerson();
//    p1.showNum(); //常对象不可以调用普通成员函数,因为普通成员函数可以修改属性
 
};
 
int main() {
    test01();
    return 0;
}
 
 

四、友元

友元:让一个函数或类,访问另一个类中私有成员;关键字为friend;

1、全局函数做友元

#include <iostream>
#include <string>
 
using namespace std;
 
//建筑物
class Building {
//    gooeGay函数是Building的好朋友,可以访问Building的私有属性
    friend void gooeGay(Building &build);
 
public:
    Building() {
        m_SittingRoom = "ke——ting";
        m_BedRoom = "wo-shi";
    }
 
public:
    string m_SittingRoom;//客厅
private:
    string m_BedRoom;//卧室
};
 
//全局函数
void gooeGay(Building &build) {
    cout << "public: " << build.m_SittingRoom << endl;
    cout << "private: " << build.m_BedRoom << endl;
}
 
void test01() {
    Building building;
    gooeGay(building);
}
 
int main() {
    test01();
    return 0;
}
 
 


public: ke——ting
private: wo-shi

2、类做友元

#include <iostream>
#include <string>
 
using namespace std;
 
class Building;
 
class GoodGay {
public:
    GoodGay();
 
public:
    void visit();
 
    Building *building;
};
 
 
class Building {
//    GoodGay是本类的好朋友,可以访问私有属性
    friend class GoodGay;
 
public:
    Building();
 
public:
    string m_SittingRoom; //客厅
private:
    string m_BedRoom;//卧室
};
 
Building::Building() {
    m_SittingRoom = "ke——ting";
    m_BedRoom = "wo-shi";
}
 
GoodGay::GoodGay() {
    building = new Building;
}
 
void GoodGay::visit() {
    cout << building->m_SittingRoom << endl;
    cout << building->m_BedRoom;
}
 
void test01() {
    GoodGay gay;
    gay.visit();
}
 
int main() {
    test01();
    return 0;
}
 
 

3、成员函数做友元

#include <iostream>
#include <string>
 
using namespace std;
 
class Building;
 
class GoodGay {
public:
    GoodGay();
 
public:
    void visit();
 
    void visit2();
 
    Building *building;
};
 
 
class Building {
//    GoodGay::visit()函数是本类的好朋友,可以访问私有属性
    friend void GoodGay::visit();
 
public:
    Building();
 
public:
    string m_SittingRoom; //客厅
private:
    string m_BedRoom;//卧室
};
 
Building::Building() {
    m_SittingRoom = "ke——ting";
    m_BedRoom = "wo-shi";
}
 
GoodGay::GoodGay() {
    building = new Building;
}
 
void GoodGay::visit() {
    cout << building->m_SittingRoom << endl;
    cout << building->m_BedRoom;
}
 
void GoodGay::visit2() {
    cout << building->m_SittingRoom << endl;
//    cout << building->m_BedRoom;
}
 
void test01() {
    GoodGay gay;
    gay.visit();
}
 
int main() {
    test01();
    return 0;
}
 
 

C++类和对象3:https://developer.aliyun.com/article/1548158?spm=a2c6h.13148508.setting.17.224d4f0e8YFWtB

目录
相关文章
|
3天前
|
C++
【C++】日期类Date(详解)②
- `-=`通过复用`+=`实现,`Date operator-(int day)`则通过创建副本并调用`-=`。 - 前置`++`和后置`++`同样使用重载,类似地,前置`--`和后置`--`也复用了`+=`和`-=1`。 - 比较运算符重载如`&gt;`, `==`, `&lt;`, `&lt;=`, `!=`,通常只需实现两个,其他可通过复合逻辑得出。 - `Date`减`Date`返回天数,通过迭代较小日期直到与较大日期相等,记录步数和符号。 ``` 这是236个字符的摘要,符合240字符以内的要求,涵盖了日期类中运算符重载的主要实现。
|
3天前
|
存储 编译器 C++
【C++】类和对象④(再谈构造函数:初始化列表,隐式类型转换,缺省值
C++中的隐式类型转换在变量赋值和函数调用中常见,如`double`转`int`。取引用时,须用`const`以防修改临时变量,如`const int& b = a;`。类可以有隐式单参构造,使`A aa2 = 1;`合法,但`explicit`关键字可阻止这种转换。C++11起,成员变量可设默认值,如`int _b1 = 1;`。博客探讨构造函数、初始化列表及编译器优化,关注更多C++特性。
|
3天前
|
编译器 C++
【C++】类和对象④(类的默认成员函数:取地址及const取地址重载 )
本文探讨了C++中类的成员函数,特别是取地址及const取地址操作符重载,通常无需重载,但展示了如何自定义以适应特定需求。接着讨论了构造函数的重要性,尤其是使用初始化列表来高效地初始化类的成员,包括对象成员、引用和const成员。初始化列表确保在对象创建时正确赋值,并遵循特定的执行顺序。
|
3天前
|
C语言 C++
【C++】日期类Date(详解)③
该文介绍了C++中直接相减法计算两个日期之间差值的方法,包括确定max和min、按年计算天数、日期矫正及计算差值。同时,文章讲解了const成员函数,用于不修改类成员的函数,并给出了`GetMonthDay`和`CheckDate`的const版本。此外,讨论了流插入和流提取的重载,需在类外部定义以符合内置类型输入输出习惯,并介绍了友元机制,允许非成员函数访问类的私有成员。全文旨在深化对运算符重载、const成员和流操作的理解。
|
3天前
|
定位技术 C语言 C++
C++】日期类Date(详解)①
这篇教程讲解了如何使用C++实现一个日期类`Date`,涵盖操作符重载、拷贝构造、赋值运算符及友元函数。类包含年、月、日私有成员,提供合法性检查、获取某月天数、日期加减运算、比较运算符等功能。示例代码包括`GetMonthDay`、`CheckDate`、构造函数、拷贝构造函数、赋值运算符和相关运算符重载的实现。
|
3天前
|
编译器 C++
【C++】类和对象③(类的默认成员函数:赋值运算符重载)
在C++中,运算符重载允许为用户定义的类型扩展运算符功能,但不能创建新运算符如`operator@`。重载的运算符必须至少有一个类类型参数,且不能改变内置类型运算符的含义。`.*::sizeof?`不可重载。赋值运算符`=`通常作为成员函数重载,确保封装性,如`Date`类的`operator==`。赋值运算符应返回引用并检查自我赋值。当未显式重载时,编译器提供默认实现,但这可能不足以处理资源管理。拷贝构造和赋值运算符在对象复制中有不同用途,需根据类需求定制实现。正确实现它们对避免数据错误和内存问题至关重要。接下来将探讨更多操作符重载和默认成员函数。
|
3天前
|
存储 编译器 C++
【C++】类和对象③(类的默认成员函数:拷贝构造函数)
本文探讨了C++中拷贝构造函数和赋值运算符重载的重要性。拷贝构造函数用于创建与已有对象相同的新对象,尤其在类涉及资源管理时需谨慎处理,以防止浅拷贝导致的问题。默认拷贝构造函数进行字节级复制,可能导致资源重复释放。例子展示了未正确实现拷贝构造函数时可能导致的无限递归。此外,文章提到了拷贝构造函数的常见应用场景,如函数参数、返回值和对象初始化,并指出类对象在赋值或作为函数参数时会隐式调用拷贝构造。
|
3天前
|
存储 编译器 C语言
【C++】类和对象②(类的默认成员函数:构造函数 | 析构函数)
C++类的六大默认成员函数包括构造函数、析构函数、拷贝构造、赋值运算符、取地址重载及const取址。构造函数用于对象初始化,无返回值,名称与类名相同,可重载。若未定义,编译器提供默认无参构造。析构函数负责对象销毁,名字前加`~`,无参数无返回,自动调用以释放资源。一个类只有一个析构函数。两者确保对象生命周期中正确初始化和清理。
|
6天前
|
存储 编译器 C语言
【C++航海王:追寻罗杰的编程之路】类与对象你学会了吗?(上)
【C++航海王:追寻罗杰的编程之路】类与对象你学会了吗?(上)
10 2
|
5天前
|
C++
C++职工管理系统(类继承、文件、指针操作、中文乱码解决)
C++职工管理系统(类继承、文件、指针操作、中文乱码解决)
7 0
C++职工管理系统(类继承、文件、指针操作、中文乱码解决)