C++——02类的基本特性

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档


前言

提示:这里可以添加本文要记录的大概内容:
例如:随着人工智能的不断发展,机器学习这门技术也越来越重要,很多人都开启了学习机器学习,本文就介绍了机器学习的基础内容。


提示:以下是本篇文章正文内容,下面案例可供参考

一、封装的过程

1.定义

1.数据访问权限

类内:类的设计者编写代买的地方
类外:类的使用者编写代码的地方
访问权限关键字只限制类外,不限制类内。
(1)pubilc:允许类外访问
(2)privent
(3)protect

2.类对象内存空间

类中数据类型所占空间按c语言结构体对齐规则计算,函数名不占空间。如果类种不存在数据类型则,只有一个字节空间为了让使用则能够调用,预留出一字节的空间。

3.构造函数

调用时无需传参的构造函数称为默认构造函数
任意对象在创造时都会触发构造函数,构造函数没有返回值,名字与类名相同。目的是为了初始化类内成员。
在使用时如果调用默认构造,则可以不用写括号。
如果要传参则写括号写默认形参。
构造函数执行的过程:
(1)传参
(2)按照数据成员声明的数据类型为数据成员开辟内存空间
(3)执行构造函数函数体
初始化列表
初始化的顺序,与数据成员声明的数据相同,放在构造函数定义的地方。

分类:
按照参数分类,可以分为有参构造函数和无参构造函数(默认)
按照类型分类,可以分为普通构造函数和拷贝构造函数

#include<iostream>
using  namespace std;
#include<string>
class Persion
{
private:
    /* data */
public:
    Persion() {/*默认构造函数*/
        cout << "this no args structure fun" << endl;
    } 
    Persion(int age) { /*复制构造函数*/
        m_age = age;
        cout << "this args structure fun" << endl;
    }
    Persion(const Persion &p) { /*必须穿传引用*/
        m_age = p.m_age;
        cout << "this copy structure fun" << endl;
    }
    ~Persion();
    int m_age;
};

// Persion::Persion(/* args */)
// {
// }

Persion::~Persion()
{
}
/* 调用 */
void test()
{
    /*1.括号法*/
    // Persion p1;
    // Persion p2(18);
    // Persion p3(p2);
    // //注意事项:
    // //不要在括号去调用默认构造函数去创建对象。编译器会认为是一个函数声明:
    // Persion p4(); //错误的示范
    // void test1(); //函数声明

    /*2.显示法*/
    // Persion p1;
    // Persion p2 = Persion(10); //有参构造
    // Persion p3 = Persion(p2); //拷贝构造
    // Persion p3(p2);
    //注意事项1
    // Persion (10);//匿名对象 特点:当前执行后编译器会立即回收匿名对象

    //注意事项2
    // 不要利用拷贝构造函数创建匿名对象,Person (p3) = Person p3;

    /*3.隐式转换法*/
    Persion p4 = 4;    //有参构造
    Persion p5 = p4;   //拷贝构造

}
int main()
{
	test();
    cout << "this main fun" << endl;
}

4.复制构造函数

复制构造函数一定是本类对象的常引用
使用范围/调用时机:
(1)使用已有的对象创建另一个对象
(2)函数值传递形参是类对象会触发复制构造函数
(3)类对象作为函数的返回值,会得到一个匿名函数。

#include<iostream>
using  namespace std;
#include<string>
class Persion
{
private:
    /* data */
public:
    Persion() {/*默认构造函数*/
        cout << "this no args structure fun" << endl;
    } 
    Persion(int age) { /*复制构造函数*/
        m_age = age;
        cout << "this args structure fun" << endl;
    }
    Persion(const Persion &p) { /*必须穿传引用*/
        m_age = p.m_age;
        cout << "this copy structure fun" << endl;
    }
    ~Persion() {
        cout << "this  destructure fun" << endl;
    }
    int m_age;
};

void do_work(Persion p) {
    cout << "this is a test" << endl;
}
/*1.用一个已有的对象进行创建*/
void test01(void) {
    Persion p1(10);
    Persion p2(p1);
}
/*2.做函数参数*/
void test02(void) {
    Persion p1;
    do_work(p1);
    
} 
Persion do_work2(void) {
    cout << "this is a test" << endl;
    Persion p1;
    cout << (int*)&p1 <<endl;
    return p1;
}
/*3.做函数返回值*/
void test03(void) {
    
    // Persion p = test02();
    Persion p2 = do_work2();
    cout << (int*)&p2 <<endl;
}
int main()
{
	// test();
    /*1.用一个已有的对象进行创建*/
    // test01();
    // test02();.
    test03();


    // test04();
   
    cout << "this main fun" << endl;
}

默认情况下,c++会给类添加至少三个函数
1.默认构造函数,函数体为空
2.默认析构函数,函数体为空
3.默认拷贝函数,对属性进行拷贝

构造函数调用规则如下:
1.如果用户定义了有参构造函数,c++不在提供默认构造函数,但是会提供拷贝构造函数
2.如果用户定义了拷贝构造函数,不会再提供其他构造函数

4.1深拷贝与浅拷贝

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

#include<iostream>
using  namespace std;
#include<string>
class Persion
{
private:
    /* data */
public:
    Persion() {/*默认构造函数*/
        cout << "this no args structure fun" << endl;
    } 
    Persion(int age,int height) { /*带参构造函数*/
        m_age = age;
        m_height = new int(height);
        cout << "this args structure fun" << endl;
    }
    Persion(const Persion &p) { /*拷贝构造函数*//*必须穿传引用*/
        m_age = p.m_age;
         m_height = new int(*p.m_height);
        cout << "this copy structure fun" << endl;
    }
    ~Persion() {
        if(!m_height) {
            delete m_height;
        }
        cout << "this  destructure fun" << endl;
    }
    int m_age;
    int *m_height;
};

void do_work(Persion p) {
    cout << "this is a test" << endl;
}
/*1.用一个已有的对象进行创建*/
void test01(void) {
    Persion p1(10,160);
    Persion p2(p1);
}
/*2.做函数参数*/
void test02(void) {
    Persion p1;
    do_work(p1);
    
} 

int main()
{
	test01();
   
    cout << "this main fun" << endl;
}

4.2初始化列表

class Test
{
private:
    /* data */
public:
    // Test(/* args */);
    ~Test();
    Test(int a, int b, int c):m_a(a),m_b(b),m_c(c){

    }
    int m_a;
    int m_b;
    int m_c;


};

Test::~Test()
{
}


int main()
{
	Test a(1 , 2, 3);

    cout << "a.m_a = "<<a.m_a<<endl;
    cout << "a.m_b = "<<a.m_b<<endl;
    cout << "a.m_c = "<<a.m_c<<endl;
    cout << "this main fun" << endl;
}

当其他类对象作为本类函数成员的时候,构造时候先构造类对象,再构造自身,构造的顺序和析构的顺序相反

4.3静态成员

  • 静态成员变量的特点
    所有的对象共享一份数据
    再编译阶段分配内存
    类内申明,类外初始化

    访问方式:
    1.通过对象进行访问
    2.通过类名进行访问
    静态成员变量有访问权限


/*类的静态成员*/
class StaticTest
{
private:
    /* data */
public:
    StaticTest(/* args */);
    ~StaticTest();
    static int m_a;
};

int StaticTest::m_a = 10;
StaticTest::StaticTest(/* args */)
{
}

StaticTest::~StaticTest()
{
}

void test_static_num(void){
    cout <<StaticTest::m_a <<endl;
}

int main()
{
    StaticTest T;
    cout <<StaticTest::m_a <<endl;
    T.m_a = 20;
    cout <<T.m_a <<endl;
	test_static_num();
}
  • 静态成员函数
    所有的对象共享一个函数
    静态成员函数只能访问静态成员变量

5.析构函数

析构函数不允许带参数。
析构函 数执行流程:
1.执行析构函数函数体
2.按照数据成员声明的数据类型的逆顺序为数据成员开辟内存空间

6.C++对象模型和this指针

c++成员变量和成员函数是分开存储,
如果是一个类的类型是成员变量是空,内存大小是1字节,是编译器为了和其他类对象进行区分,创建的。

在类成员函数系统会默认传一个THIS指针
用途是:
1.解决名称冲突
2.返回对象本身用*this

class ThisPiont
{
private:
    /* data */
public:
    ThisPiont(/* args */);
    ThisPiont(int num){
        /*1.用于区别*/
        this->num = num;
    }
    ~ThisPiont();
    ThisPiont& add_fun(ThisPiont p) {
        this->num += p.num;
        return *this;/*指向函数本身*/
    }
    int num;
};

ThisPiont::ThisPiont(/* args */)
{
}

ThisPiont::~ThisPiont()
{
}


int main()
{
    ThisPiont p1(10);
    ThisPiont p2(10);
    // test.add_fun(test);
    /*链式编程思想,类似与cout*/
    p2.add_fun(p1).add_fun(p1).add_fun(p1);
    cout << "p1 test.num" <<p1.num <<endl;
    cout << "p2 test.num" <<p2.num <<endl;
}

6.1 this如果是空

class ThisPiont
{
private:
    /* data */
public:
    ThisPiont(/* args */);
    ThisPiont(int num){
        /*1.用于区别*/
        this->num = num;
    }
    ~ThisPiont();
    ThisPiont& add_fun(ThisPiont p) {
        this->num += p.num;
        return *this;/*指向函数本身*/
    }
    void fun1(void) {
        cout << "this fun1 " << endl;
    }
    void fun2(void) {
        if (this == NULL) {
            return;
        }
        cout << "this fun2" << num <<endl;
    }
    int num;
};

ThisPiont::ThisPiont(/* args */)
{
}

ThisPiont::~ThisPiont()
{
}


int main()
{
    ThisPiont p1(10);
    ThisPiont p2(10);
    ThisPiont *test = NULL;
    test->fun1();
    test->fun2();
}

6.2 this 指针本质

是一个指针常量,指针指向不可以修改
类似与于 Person *const this;

6.3 常函数

class ThisPiont
{
private:
    /* data */
public:
    ThisPiont(/* args */);
    ThisPiont(int num){
        /*1.用于区别*/
        this->num = num;
    }
    ~ThisPiont();
    ThisPiont& add_fun(ThisPiont p) {
        this->num += p.num;
        return *this;/*指向函数本身*/
    }
    /*常函数:在函数后面加const ,修饰的是this指针的指向,让指针指向的值也不可以修改*/
    void showPont(void) const
    {
        // this->num = 100;/*error*/
        this->numtest = 100;
    }

    void fun() {

    }
    int num;
    mutable int numtest;/*在常函数中,加入mutable 可以被修改*/
};

ThisPiont::ThisPiont(/* args */)
{
}

ThisPiont::~ThisPiont()
{
}

void test03(void) {
    const ThisPiont test;
    test.showPont();/*常对象只能调用常函数*/
    //test.fun();/*error*/
}

int main()
{
   
   test03();
}

6.3友元函数

共有以下几类:
1.全局函数:将函数的声明在类内,并且在前面加一个friend,这样全局函数就可以访问,类内的私有成员

friend void test(void);

2.类做友元:将类的声明在类内,并且在前面加一个friend,另一个类就可以访问,类内的私有成员

friend class TEST;

3.成员函数:将类的成员函数的声明在类内,并且在前面加一个friend,这样全局函数就可以访问,类内的私有成员
TEST 类下面的test函数可以访问声明了类的私有成员

friend TEST::void test(void);

7.类的组合

如果某个类当中的数据成员要其带参构造构造时,使用初始化列表向其传参。

8.系统提供的类函数

	1.默认构造函数
	2.默认复制构造函数
	3.默认析构函数
	4.默认复制运算符重载函数
	5.非常对象取地址运算符重载
	6.常对象取地址运算符重载
	7.移动复制运算符
	8.移动复制构造函数
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值