C++ 类、类实例化对象、多文件编程

1. 对象

1.1 为什么要面向对象

在这里插入图片描述

1.2 什么是面向对象

在这里插入图片描述

2. 类

在这里插入图片描述

  • 从语法角度来说是自定义的数据类型,类似之前所说的结构体,但比结构体要复杂。成员变量成员函数表达对象的属性行为
  • 上图中左边是一群狗,编程时把他们共同的属性行为提取出来就得到了。然后再用类实例化出具体的对象,得到右边各种各样的狗。

2.1 类的一般形式

在这里插入图片描述

  • 定义类 (原来叫定义结构体, 现在叫定义类) 一般都用class, 保留struct是为了兼容c中的结构体。

2.1.1 代码示例

#include <iostream>
using namespace std;

// 定义一个类:Student
struct Student {
	// string 这个类定义的对象比较大, 这里加&提高传参效率,避免实参到形参的拷贝
	// const是为了再提高传参效率的同时,还可以接收常量型的实参(右值?)
    void eat(const string& food){
        cout << "i am eating " << food <<endl;
    }
	// int 只有4个字节,加不加&都不会太影响传参效率
    void sleep(int hour) {
        cout << "i have slpeeped " << hour << endl;
        cout << m_age << endl;
    }
    // who 属于类中的一个成员函数,可以直接访问成员函数
    void who(void) {
        cout << "my name is " << m_name << ", i am " << m_age << " years old, " 
            << "my id is " << m_no << endl;
    }

    string m_name;
    int m_age;
    int m_no;
};

int main(void){
    // 用 Student 类 实例化一个对象
    Student s;
    s.m_name = "xuehui";
    s.m_age = 25;
    s.m_no = 666;
    s.who();
    s.eat("rice");
    s.sleep(8);
    return 0;
}
$ ./a.out 
my name is xuehui, i am 25 years old, my id is 666
i am eating rice
i have slpeeped 8

2.2 访问控制限定符

在这里插入图片描述

2.2.1 代码示例

#include <iostream>
using namespace std;

// 使用class定义一个类:Student
// 它的缺省访问控制属性是private, 只有类内部才可以访问类的成员。
class Student {
// 访问控制属性改成public以后, 下面的main函数中才能直接访问对象s的成员变量
public: 
	// string 这个类定义的对象比较大, 这里加&提高传参效率,避免实参到形参的拷贝
	// const是为了再提高传参效率的同时,还可以接收常量型的实参(右值?)
    // 在类的外面可以直接调用 类中公有成员函数 setName 
    // 来访问私有成员变量 name。 这就是封装
    // 对非法数据加以限定,增加的程序的安全以及逻辑合理性。
    void setName(const string& name){
        if (name == "sb"){
            cout << "you are sb!" << endl;
        }
        else
        {
            m_name = name;
        }
    }

private:
    string m_name;
};

int main(void){
    // 用 Student 类 实例化一个对象 s
    Student s;
    s.setName("sb");
    return 0;
}
$ ./a.out 
you are sb!

2.3 构造函数

在这里插入图片描述

  • 使用构造函数是为了在创建对象的时候希望有一个确定的初始化状态。
  • 可以把构造函数理解为创建对象时一定会被调用的一个成员函数。一定会被调用,且只会被调用一次。

2.3.1 代码示例

#include <iostream>
using namespace std;

// 使用class定义一个类:Student
// 它的缺省访问控制属性是private, 只有类内部才可以访问类的成员。
class Student {
// 访问控制属性改成public以后, 下面的main函数中才能直接访问对象s的成员变量
public: 
	// string 这个类定义的对象比较大, 这里加&提高传参效率,避免实参到形参的拷贝
	// const是为了再提高传参效率的同时,还可以接收常量型的实参(右值?)
    Student(const string& name){
        cout << "constructor" << endl;
        m_name = name;
    }

    void who(void) {
        cout << "my name is " << m_name << endl;
    }

private:
    string m_name;
};

int main(void){
    // 用 Student 类 实例化一个对象 s, 期间会自动调用构造函数
    // 所以要指明构造函数需要的实参
    Student s("xuehui");
    s.who();
    // s.Student("test"); 构造函数不能显示调用
    return 0;
}
$ ./a.out 
constructor
my name is xuehui

2.3.2 用类实现一个时钟

#include <iostream>
#include <ctime>
#include <cstdio>
#include <unistd.h>
using namespace std;

class Clock {
public:
    // 构造函数初始化当前时间. 用到的函数:man 2 time, man 3 localtime
    Clock(time_t t){
        // 定义tm结构体类型变量相比C直接省略struct
        tm* local = localtime(&t);
        m_hour = local->tm_hour;
        m_min = local->tm_min;
        m_sec = local->tm_sec;
    }

    // 自动刷新时间
    void run(void){
        while(1){
            // \r 光标停在行首。%02 站两位,不够前面补零。
            printf("\r%02d:%02d:%02d",m_hour, m_min, m_sec);
            // 因为上面没有\n, 不会立马输出
            // fflush 刷新标准输出缓冲区,上面printf的内容会立马输出到屏幕
            fflush(stdout);
            if(++m_sec == 60){
                m_sec = 0;
                if(++m_min == 60){
                    m_min = 0;
                    if(++m_hour == 24){
                        m_hour = 0;
                    }
                }
            }
            sleep(1);
        }
    }

private:
    int m_hour;
    int m_min;
    int m_sec;
};

int main(void){
    Clock c(time(NULL));
    c.run();
    return 0;
}
$ ./a.out 
12:35:06

3. 对象的创建和销毁

3.1 在栈区创建对象

在这里插入图片描述

  • 类名 对象 = 类名(实参表): 这是拷贝初始化的写法,用实参表先构造一个匿名对象,再对=左边的对象进行拷贝初始化,然后再把匿名对象释放掉。(有点类似临时变量)

3.1.2 代码示例

#include <iostream>
using namespace std;


class Student {
public: 
	// 构造函数的重载
    Student(void){
        cout << "constructor_1" << endl;
        m_name = "x";
    }

    Student(const string& name){
        cout << "constructor_2" << endl;
        m_name = name;
    }

    void who(void) {
        cout << "my name is " << m_name << endl;
    }

private:
    string m_name;
};

int main(void){
    // 1. 在栈区创建单个对象
    // 1.1 以无参方式创建对象
    Student s1; // Student s1(); 不可以这样写, 编译器会把s1当成返回值是Student类型的函数
    s1.who();
    // 1.2 以有参方式创建对象
    Student s2("xuehui");
    s2.who();
    // 1.3 另一种写法: 拷贝初始化
    Student s3 = Student("xuehui");
    s3.who();

    // 2. 在栈区创建对象数组
    // 2.1 无参方式
    Student sarr1[1];
    sarr1[0].who();
    // 2.2 有参方式
    Student sarr2[2] = {Student("xuehui"), Student("xuehui_1")};
    sarr2[0].who();
    sarr2[1].who();
    return 0;
}
$ ./a.out
constructor_1
my name is x
constructor_2
my name is xuehui
constructor_2
my name is xuehui
constructor_1
my name is x
constructor_2
constructor_2
my name is xuehui
my name is xuehui_1

3.2 在堆区创建/销毁对象

在这里插入图片描述

  • new 在堆区完成内存分配,还会自动去调用构造函数,完车对象的初始化。而malloc只会完成内存分配。
  • 同样的如果delete的是一个对象类型的指针,除了会完成动态资源的释放,它还会去调用这个对象对应类的析构函数。把这个对象的维护的动态资源释放掉。而 free只是会完成动态资源的释放。
#include <iostream>
using namespace std;


class Student {
public: 
	// 构造函数的重载
    Student(void){
        cout << "constructor_1" << endl;
        m_name = "x";
    }

    Student(const string& name){
        cout << "constructor_2" << endl;
        m_name = name;
    }

    void who(void) {
        cout << "my name is " << m_name << endl;
    }

private:
    string m_name;
};

int main(void){
    // 1. 在堆区创建单个对象
    // 1.1 无参方式
    Student* ps1 = new Student;
    ps1->who();
    delete ps1;
    // 1.2 有参方式
    Student* ps2 = new Student("xuehui");
    ps2->who();
    delete ps2;

    // 2. 在堆区创建多个对象
    // 2.1 无参方式
    // parr 相当于数组名,指向数组第一个元素
    Student* parr1 = new Student[2];
    (parr1+0)->who();
    (parr1+1)->who();
    delete [] parr1;
    // 2.2 有参方式
    Student* parr2 = new Student[2]{Student("xuehui_1"), Student("xuehui_2")};
    (parr2+0)->who();
    (parr2+1)->who();
    delete [] parr2;
    return 0;
}
$ ./a.out
constructor_1
my name is x
constructor_2
my name is xuehui
constructor_1
constructor_1
my name is x
my name is x
constructor_2
constructor_2
my name is xuehui_1
my name is xuehui_2

4. 多文件编程

4.1 类的声明、实现 可以分开写

Student.h

#ifndef __STUDNET__
#define __STUDNET__

#include <iostream>
using namespace std;

class Student {
public: 
    Student(const string& name);
    void who(void);

private:
    string m_name;
};

#endif // __STUDNET__

Student.cpp

#include "Student.h"

// 需要在函数名前加上"类名:",指明它是类中的成员函数
Student::Student(const string& name){
    m_name = name;
}

void Student::who(void) {
    cout << "my name is " << m_name << endl;
};


main.cpp

#include "Student.h"
int main(void){
    Student s("xuehui");
    s.who();
    return 0;
}
$ g++ main.cpp  Student.cpp
$ ./a.out
my name is xuehui
  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: NJ501是一个路径规划编程实例的题目。在这个编程实例,我们要解决的问题是如何找到两个地点之间的最短路径。 这个编程实例涉及到图的基本概念和算法,我们需要建立一个图并使用适当的算法来寻找最短路径。 首先,我们需要定义一个城市网络的图。每个城市作为图的一个顶点,而城市之间的道路则表示为两个顶点之间的边。在图,我们还需要为每条边分配适当的权值,可以使用道路的长度或者行驶时间等作为权值。 接下来,我们可以使用Dijkstra算法或者A*算法来计算最短路径。Dijkstra算法是一种贪婪算法,它根据每个节点的距离去选择下一个节点,直到找到最短路径。A*算法结合了贪婪算法和启发式搜索,它利用了一些启发式函数来优化路径的选择。 在编程实现,我们可以使用图的邻接表或邻接矩阵来表示图,并使用优先队列来实现Dijkstra算法或A*算法的优化。 最后,我们以一个示例来说明这个编程实例。假设我们有一个城市网络,其包含不同的城市和连接它们的道路。我们想要从城市A到城市B的最短路径。我们可以使用上述算法来计算最短路径,并返回路径的长度或者具体的路径。 通过这个编程实例,我们能够掌握图的基本概念以及两个地点之间最短路径的计算方法。这对于解决实际生活的路径规划问题非常有用。 ### 回答2: nj501是一个用于学习和实践编程的平台,其包含了1500个不同的编程实例。 这些编程实例涵盖了多个不同的题和难度级别,适合从初学者到进阶者的编程者。这些实例涉及的编程语言包括C,C++,Java,Python等,并且提供了相应的代码和解决方案,以帮助用户更好地理解和掌握编程。 nj501编程实例的内容十分全面和多样化。其包括基本的编程概念和技巧,如变量,循环,数组等,以及高级的题,如数据结构,算法,面向对象编程等。此外,还涉及到一些实际应用场景的编程问题,如文件处理,网络通信等。 每个编程实例都配有详细的说明和要求,用户可以根据自己的需求选择感兴趣的实例进行学习和实践。通过完成这些实例,用户可以提升自己的编程能力和解决问题的能力,同时也增强了对编程语言和工具的熟悉程度。 总之,nj501提供了丰富且全面的编程实例,帮助用户深入学习和实践编程。无论是初学者还是进阶者,无论是对某个具体编程语言感兴趣还是想提升自己的编程能力,nj501都是一个非常好的选择。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值