C++中的类详解(1)

知识点1【封装】

  1. 把变量和函数合成一个整体,封装在一个类中
  2. 对变量和函数进行访问控制(公有、私有、保护)
访问属性属性对象内部对象外部
public公有可访问可访问
protected保护可访问不可访问
private私有可访问不可访问

在没有涉及继承与派生时,protected与private 是同等级的,外部不允许访问。

知识点2【类的初识】

#include <iostream>
#include<cstring>
using namespace std;
class Person{
private:
    char name[20];//定义类的时候,不要给成员初始化
    int age;
public:
    void setNmae(char* n){
        strcpy(name,n);
    }
    char* getName(){
        return name;
    }
    void setAge(int a){
        age = a;
    }
    int getAge(){
        return age;
    }
};

int main(int argc, char *argv[])
{
    Person person;
    char name[20] = "tony";
    person.setNmae(name);
    person.setAge(15);
    cout << person.getName() << person.getAge() << endl;
    return 0;
}

实例:设计一个点和圆的类

#include <iostream>

using namespace std;
class Point{//设计一个点的类
private:
    int m_x;
    int m_y;
public:
    void setX(int x){
        m_x = x;
    }
    int getX(){
        return m_x;
    }
    void setY(int y){
        m_y = y;
    }
    int getY(){
        return m_y;
    }
};

class Circle{//设计一个圆的类
private:
    Point point;
    int r;
public:
    void setPoint(int x,int y){
        point.setX(x);
        point.setY(y);
    }

    void setR(int x){
        r = x;
    }
    void isPointOnCircle(Point &ob){
        //判断点与圆的关系
        int distance = (point.getX()-ob.getX()) *(point.getX()-ob.getX()) + (point.getY() - ob.getY()) * (point.getY() - ob.getY());
        if(distance > r*r){
            cout << "在圆外" << endl;
        }else if(distance == r*r){
            cout << "在圆上" << endl;
        }else{
            cout << "在圆内" << endl;
        }
    }

};

int main(int argc, char *argv[])
{
	//实例化一个点对象
    Point p1;
    p1.setX(5);
    p1.setY(5);
    
    //实例化一个圆对象
    Circle circle;
    circle.setR(3);
    circle.setPoint(3,3);
    //判断点point与圆的关系
    circle.isPointOnCircle(p1);
    return 0;
}

知识点3【类的空间大小】

成员数据占类的空间大小,成员函数不占类空间的大小。

using namespace std;
 class Data
 {
 private:
 //成员数据 占类的空间大小
 int num;//4B
 public:

 //成员函数 不占类的空间大小
 void setNum(int data)
 {
 num = data;
 }
 int getNum(void)
 {
 return num;
 }
 };
 void test01()
 {
 printf("%d\n",sizeof(Data));//4B
 }

知识点4【在类内声明 类外定义 成员函数】

class data{
private:
    int num;
public:
    void setNum(int x);
    int getNum();
};

void Data::setNum(int x){
     num = x;
 }
 
int Data::getNum(){
     return num;
 }

void test01(){
     Data data;
     data.setNum(100);
     cout << data.getNum() << endl;
 }
int main(int argc, char *argv[]){
    test01();
    return 0;
}

知识点5【类的定义在头文件 成员函数 在cpp文件中实现】

data1.h

#ifndef DATA1_H
#define DATA1_H


class Data1
{
private:
    int num;
public:
    void setNum(int x);
    int getNum();
};

#endif // DATA1_H

data1.cpp

#include "data1.h"

void Data1::setNum(int x){
    num = x;
}

int Data1::getNum(){
    return num;
}

main.cpp

#include <iostream>
#include"data1.h"

using namespace std;

void test01(){
     Data1 data;
     data.setNum(1000);
     cout << data.getNum() << endl;
 }

int main(int argc, char *argv[]){
    test01();
    return 0;
}

知识点6【构造和析构函数的概述】

构造函数和析构函数都会被编译器自动调用,构造函数完成对象的初始化动作,析构函数在对象结束的时候完成清理工作。

注意:初始化和清理工作是编译器强制我们做的事情,即使程序并没有提供初始化操作和清理操作,编译器也会给程序增加默认的操作,但是默认操作并不会做任何事。

构造函数:实例化对象的时候系统自动调用
析构函数:对象释放的时候系统自动调用

知识点7【构造和析构函数的定义】

构造函数的语法:
构造函数名与类名相同,没有返回类型,连void都不可以,但可以有参数,可以重载。
析构函数的语法:
析构函数的名字是在类名前面加“ ~ ”组成,没有返回类型,连void都不可以,不能有参数,不能重载。
案例:

#include <iostream>

using namespace std;

class Data
{
public:
    Data() {
        cout << "无参构造函数" << endl;
    }

    Data(int num){
        num = 100;
        cout << "有参构造函数" << endl;
    }

    ~Data(){
        cout << "析构函数" << endl;
    }
};

int main(int argc, char *argv[])
{
    cout << "-----------01----------" << endl;
    Data ob;
    cout << "-----------02----------" << endl;
    return 0;
}

知识点8【构造函数的分类以及调用】

1、构造函数的分类

按参数类型分类: 分为无参构造函数和有参构造函数
按类型分类:普通构造函数和拷贝构造函数(复制构造函数)

2、构造函数的调用

#include <iostream>

using namespace std;

class Data
{
private:
    int num;
public:
    Data() {
        num = 0;
        cout << "无参构造函数" << endl;
    }

    Data(int num1){
        num = num1;
        cout << "有参构造函数" << num << endl;
    }

    ~Data(){
        cout << "析构函数" << num << endl;
    }
};
void test01(){
    //调用无参或默认构造函数
    Data ob;
    //调用有参构造函数(显式调用)
    Data ob1 = Data(100);
    //调用有参构造函数(隐式调用)
    Data ob2(1000);
    //隐式转换的方式 调用有参构造(针对于 只有一个数据成员)(尽量别用)
    Data ob3 = 10000;//转化成Data ob5(30)

    //匿名对象(当前语句结束 匿名对象立即释放)
    Data(40);
    cout << "---------------------" << endl;

    //千万不要 用以下方式调用 无参构造
    //Data ob4();//不会被认为时实例化对象,会被看作函数ob4的声明
}

int main(int argc, char *argv[])
{
    cout << "-----------01----------" << endl;
    test01();
    cout << "-----------02----------" << endl;
    return 0;
}

在这里插入图片描述
注意:在同一作用域,构造和析构的顺序相反(类似于栈,先进后出)

3、拷贝构造函数(系统提供一个拷贝构造函数 赋值操作)

#include <iostream>

using namespace std;

class Data
{
public:
    int num;
public:
    Data() {
        num = 0;
        cout << "无参构造函数" << endl;
    }

    Data(int num1){
        num = num1;
        cout << "有参构造函数" << num << endl;
    }

    //拷贝构造函数
    Data(const Data &ob){//Data &ob = ob5;
        num = ob.num;
        cout << "拷贝构造函数" <<endl;
    }

    ~Data(){
        cout << "析构函数" << num << endl;
    }
};
void test01(){

    //调用有参构造函数(隐式调用)
     Data ob3 = Data(100);
     cout<<"ob3.num = "<<ob3.num<<endl;

    //显式调用拷贝构造函数
    Data ob5 = Data(ob3);
    cout<<"ob5.num = "<<ob5.num<<endl;

    //隐式调用拷贝构造函数
    Data ob6(ob3);
    cout<<"ob6.num = "<<ob6.num<<endl;

    //隐式转换调用拷贝构造函数
    Data ob7 = ob3;
    cout<<"ob6.num = "<<ob6.num<<endl;
}

int main(int argc, char *argv[])
{
    cout << "-----------01----------" << endl;
    test01();
    cout << "-----------02----------" << endl;
    return 0;
}

记住一句话:旧对象初始化新对象才会调用拷贝构造函数

Data ob1(10);
Data ob2(ob1);//拷贝构造
Data ob3 = Data(ob1);//拷贝构造
Data ob4 = ob1;//拷贝构造函数

注意:下方就不会调用拷贝构造

Data ob1(10);
Data ob2;
ob2 = ob1;//不会调用拷贝构造 单纯对象 赋值操作

案例:

void test02(){
     Data ob1 = Data(100);
     Data ob2;
     ob2 = ob1;
     cout<<"ob1.num = "<<ob1.num<<endl;
     cout<<"ob2.num = "<<ob2.num<<endl;



}

int main(int argc, char *argv[])
{
    cout << "-----------01----------" << endl;
    test02();
    cout << "-----------02----------" << endl;
    return 0;
}

在这里插入图片描述

4、拷贝函数的注意事项

(1)不能调用拷贝构造函数区初始化匿名对象
(2) 对象作为函数的参数 如果实参与形参都是普通对象 那么就会调用拷贝构造
(3)函数返回局部对象,在qt中会被优化,从而调用不了拷贝构造

知识点9【构造函数的调用规则】

系统默认会对任何一个类提供三个函数成员函数:
默认构造函数(空) 默认析构函数(空) 默认拷贝构造函数(浅拷贝)
1、如果用户提供了有参构造,将会屏蔽系统的默认构造函数
2、如果用户提供了有参构造,不会屏蔽系统的默认拷贝构造函数
3、如果用户提供了拷贝构造函数,将会屏蔽系统的默认构造函数和默认拷贝构造函数

知识点10【深拷贝与浅拷贝】

1、浅拷贝
浅拷贝只复制某个对象的引用,而不复制对象本身,新旧对象还是共享同一块内存
2、深拷贝
深拷贝会创造一个一摸一样的对象,新对象和原对象不共享内存,修改新对象不会改变原对对象。

浅拷贝:

#include <iostream>
#include<string.h>

using namespace std;


class Person{
private:
    char *m_name;
    int num;
public:

    Person(int a, char *name){
        num = a;
        m_name = new char[strlen(name)+1];
        strcpy(m_name,name);
        cout << "有参构造" <<endl;
    }


    ~Person(){
        delete [] m_name;
        cout << "空间已被释放" << endl;
        cout << "析构函数" << endl;
    }

    void setNum(int a){
        num = a;
    }
    void setString(char* str){
         strcpy(m_name,str);
    }

    void showPerson(){
        cout << m_name << "  "<< num << endl;
    }


};
int main(int argc, char *argv[])
{
    Person person = Person(100,"lucy");
    Person bob1 = person;
    cout<<"------person输出值-------"<<endl;
    person.showPerson();
    person.setNum(1000);
    person.setString("lucy2");//修改person中m_num的值
    cout<<"------person输出值-------"<<endl;
    person.showPerson();
    cout<<"------bob1输出值-------"<<endl;
    bob1.showPerson();

    return 0;
}

运行结果:
在这里插入图片描述

使用person去初始化bob1调用拷贝构造函数,我们的目的是将person中的内容拷贝到bob1的空间中,但实际上,使用浅拷贝的方式只是改变了bob1的指向,让bob1指向person所指向的空间(person和bob1的字符串m_name指向堆区同一块空间),因此,当使用函数person.setString(“lucy2”)对字符串进行修改后,bob字符串的值也会被修改,;这会使得调用析构函数时,同一空间被释放两次,造成错误;
深拷贝:

#include <iostream>
#include<string.h>

using namespace std;


class Person{
private:
    char *m_name;
    int num;
public:

    Person(int a, char *name){
        num = a;
        m_name = new char[strlen(name)+1];
        strcpy(m_name,name);
        cout << "有参构造" <<endl;
    }
    Person(const Person &ob){
        num = ob.num;
        m_name = new char[strlen(ob.m_name)+1];
        strcpy(m_name,ob.m_name);
        cout << "有参构造" <<endl;
    }


    ~Person(){
        delete [] m_name;

        cout << "空间已被释放" << endl;
        cout << "析构函数" << endl;
    }

    void setNum(int a){
        num = a;
    }
    void setString(char* str){
         strcpy(m_name,str);
    }

    void showPerson(){
        cout << m_name << "  "<< num << endl;
    }


};


int main(int argc, char *argv[])
{
    Person person = Person(100,"lucy");
    //Person bob = Person(person);
    Person bob1 = person;
    cout<<"------person输出值-------"<<endl;
    person.showPerson();
    person.setNum(1000);
    person.setString("lucy2");
    //bob.showPerson();
    cout<<"------person输出值-------"<<endl;
    person.showPerson();
    cout<<"------bob1输出值-------"<<endl;
    bob1.showPerson();

    return 0;
}

运行结果:
在这里插入图片描述
从代码运行结果来看,我们可以通过重新构建拷贝构造函数的方式,完成深拷贝的工作,就算person对象对值进行修改,也不会影响bob的值。
注意:如果类中的成员指向了堆区空间,一定要记得在析构函数中释放空间。如果用户不实现拷贝构造,系统就会提供默认拷贝构造值,而默认拷贝构造只是单纯的赋值,如果赋值为指针的话,则两个对象会指向同一块堆区空间,容易造成浅拷贝的问题。用户记得要实现:无参构造(初始化数据)、有参构造(赋参数)、拷贝构造(深拷贝)、析构函数(释放空间)。

知识点11【初始化列表】

注意:初始化成员列表(参数列表)只在构造函数使用

#include <iostream>

using namespace std;

class Data{
public:
    int m_a;
    int m_b;
    int m_c;

    Data(int a,int b,int c):m_a(a),m_b(b),m_c(c){
        cout<<"有参构造函数"<<endl;
    }
    ~Data(){
         cout<<"析构函数"<<endl;
    }
    void showData(){
        cout << m_a << " " << m_b << " " << m_c << endl;
    }
};

int main(int argc, char *argv[])
{
    Data data = Data(10,100,1000);
    data.showData();
    return 0;
}

知识点12【类对象作为另一个类的成员】

#include <iostream>

using namespace std;



class A
{
    private:
    int m_a;
public:
    A()
    {
        cout<<"A无参构造函数"<<endl;
    }
    A(int a)
    {
        m_a = a;
        cout<<"A有参构造函数"<<endl;
    }
    ~A()
    {
        cout<<"A析构函数"<<endl;
    }
};
class B
{
    private:
    int m_b;
public:
    B()
    {
        cout<<"B无参构造函数"<<endl;
    }
B(int b)
{
    m_b = b;
    cout<<"B有参构造函数"<<endl;
}
~B()
{
    cout<<"B析构函数"<<endl;
}
};

class Data
{
private:
    A oba;//对象成员
    B obb;//对象成员
    int data;//基本类型成员
public:
    Data()
    {
        cout<<"Data无参构造"<<endl;
    }

        //初始化列表:对象名+() 显示调用 调用对象成员的构造函数
    Data(int a, int b, int c):oba(a),obb(b),data(c)
    {
        //data =c;
        cout<<"Data有参构造"<<endl;
    }
    ~Data()
    {
        cout<<"Data析构函数"<<endl;
    }

};
void test01()
{
    //先调用 对象成员的构造‐‐>自己的构造函数‐‐>析构自己‐‐‐>析构对象成员
    //Data ob1;

    //系统默认调用的是 对象成员的无参构造
    //必须在Data的构造函数中 使用初始化列表 使其对象成员 调用有参构造
    Data ob2 = Data(10,20,30);

}
int main(int argc, char *argv[])
{

    test01();
    return 0;
}

注意:类中的成员变量中如果有对象实例化的对象,则在对该类进行实例化时,应该先满足实例化的对象的构造函数条件。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值