-
霸道总裁的员工:类
我们上一小节中介绍了面向对象的思想,这一小节开始,我们来具体看看在 C++ 中应该如何实现面向对象。
类
类,是 C++ 实现面向对象最基础的部分。类其实和之前学过的结构体十分相似,你可以认为类是结构体的升级版。
类的申明
在 C++ 中,可以用下面的代码申明一个员工类:
class Staff { };
可以像使用结构体一样使用这个类:
#include <stdio.h> class Staff { }; int main(int argc,char **argv) { Staff st; return 0; }
分文件编程
我们在此之前都是把代码放到一个文件里,但是这样在实际工程中肯定是不行的,我们不可能把所有的代码都写到一个文件夹里面。而在 C++ 中我们就常常把类定义到不同的文件里面,把每个类都独立起来,这样代码的耦合性就会降低,方便维护。
在 C++ 中,我们可以把一个类写到两个文件里面,一个是后缀为 .h 或者 .hpp 的头文件,一个是后缀为 .cpp 的实现文件。我们先在开发环境里新建一个类。输入类名是 Staff。
可以看到 VS 为我们创建类两个文件,Staff.h 和 Staff.cpp。Staff.h 文件为定义,Staff.cpp 为实现。
在分了文件之后,我们想要在 main 函数中引用这个类,就需要使用 #include “Staff.h” 将头文件引入进来。
实例化
在新建了一个类之后,我们就可以根据这个类产生对象了。根据类产生对象的过程叫做实例化。
#include "Staff.h" int main(int argc,char **argv) { // 我们就这样实例化了三个员工 Staff st1; Staff st2; Staff st3; return 0; }
这样分配,我们将这三个“员工”分配到了栈上,同样的,可以把他们分配到堆内存上面去。
new delete
要将对象分配到堆上,需要用到另外两个关键字,new 和 delete。new 用来分配对象,delete 用来删除对象。new 会返回一个指针,在使用完毕后,要通过 delete 把这个指针指向的地址释放掉。
#include "Staff.h"
int main(int argc,char **argv) {
Staff * st1 = new Staff();
Staff * st2 = new Staff();
Staff * st3 = new Staff();
// 记得释放
delete st1;
delete st2;
delete st3;
return 0;
}
查看全部 -
霸道总裁的员工:类
我们上一小节中介绍了面向对象的思想,这一小节开始,我们来具体看看在 C++ 中应该如何实现面向对象。
类
类,是 C++ 实现面向对象最基础的部分。类其实和之前学过的结构体十分相似,你可以认为类是结构体的升级版。
类的申明
在 C++ 中,可以用下面的代码申明一个员工类:
class Staff { };
可以像使用结构体一样使用这个类:
#include <stdio.h> class Staff { }; int main(int argc,char **argv) { Staff st; return 0; }
分文件编程
我们在此之前都是把代码放到一个文件里,但是这样在实际工程中肯定是不行的,我们不可能把所有的代码都写到一个文件夹里面。而在 C++ 中我们就常常把类定义到不同的文件里面,把每个类都独立起来,这样代码的耦合性就会降低,方便维护。
在 C++ 中,我们可以把一个类写到两个文件里面,一个是后缀为 .h 或者 .hpp 的头文件,一个是后缀为 .cpp 的实现文件。我们先在开发环境里新建一个类。输入类名是 Staff。
可以看到 VS 为我们创建类两个文件,Staff.h 和 Staff.cpp。Staff.h 文件为定义,Staff.cpp 为实现。
在分了文件之后,我们想要在 main 函数中引用这个类,就需要使用 #include “Staff.h” 将头文件引入进来。
实例化
在新建了一个类之后,我们就可以根据这个类产生对象了。根据类产生对象的过程叫做实例化。这个过程就像是公司招聘员工一样,幸运的是,我们作为程序的老板,并不需要和现实中一样去张贴招聘启示。在 C++ 中,“招聘“员工,只需要用以下的代码就可以了。
#include "Staff.h" int main(int argc,char **argv) { // 我们就这样实例化了三个员工 Staff st1; Staff st2; Staff st3; return 0; }
这样分配,我们将这三个“员工”分配到了栈上,同样的,可以把他们分配到堆内存上面去。
new delete
要将对象分配到堆上,需要用到另外两个关键字,new 和 delete。new 用来分配对象,delete 用来删除对象。new 会返回一个指针,在使用完毕后,要通过 delete 把这个指针指向的地址释放掉。
#include "Staff.h" int main(int argc,char **argv) { // 我们就这样实例化了三个员工 Staff * st1 = new Staff(); Staff * st2 = new Staff(); Staff * st3 = new Staff(); // 记得释放 delete st1; delete st2; delete st3; return 0; }
查看全部 -
霸道总裁的员工:类
我们上一小节中介绍了面向对象的思想,这一小节开始,我们来具体看看在 C++ 中应该如何实现面向对象。
类
类,是 C++ 实现面向对象最基础的部分。类其实和之前学过的结构体十分相似,你可以认为类是结构体的升级版。之后的学习中你会更加理解类。现在,我们只简单得来介绍一下类。
类的申明
在 C++ 中,可以用下面的代码申明一个员工类:
class Staff { };
可以像使用结构体一样使用这个类:
#include <stdio.h> class Staff { }; int main(int argc,char **argv) { Staff st; return 0; }
分文件编程
我们在此之前都是把代码放到一个文件里,但是这样在实际工程中肯定是不行的,我们不可能把所有的代码都写到一个文件夹里面。而在 C++ 中我们就常常把类定义到不同的文件里面,把每个类都独立起来,这样代码的耦合性就会降低,方便维护。
在 C++ 中,我们可以把一个类写到两个文件里面,一个是后缀为 .h 或者 .hpp 的头文件,一个是后缀为 .cpp 的实现文件。我们先在开发环境里新建一个类。输入类名是 Staff。
可以看到 VS 为我们创建类两个文件,Staff.h 和 Staff.cpp。Staff.h 文件为定义,Staff.cpp 为实现。
在分了文件之后,我们想要在 main 函数中引用这个类,就需要使用 #include “Staff.h” 将头文件引入进来。
实例化
在新建了一个类之后,我们就可以根据这个类产生对象了。根据类产生对象的过程叫做实例化。这个过程就像是公司招聘员工一样,幸运的是,我们作为程序的老板,并不需要和现实中一样去张贴招聘启示。在 C++ 中,“招聘“员工,只需要用以下的代码就可以了。
#include "Staff.h" int main(int argc,char **argv) { // 我们就这样实例化了三个员工 Staff st1; Staff st2; Staff st3; return 0; }
这样分配,我们将这三个“员工”分配到了栈上,同样的,可以把他们分配到堆内存上面去。
new delete
要将对象分配到堆上,需要用到另外两个关键字,new 和 delete。new 用来分配对象,delete 用来删除对象。new 会返回一个指针,在使用完毕后,要通过 delete 把这个指针指向的地址释放掉。
#include "Staff.h" int main(int argc,char **argv) { // 我们就这样实例化了三个员工 Staff * st1 = new Staff(); Staff * st2 = new Staff(); Staff * st3 = new Staff(); // 记得释放 delete st1; delete st2; delete st3; return 0; }
查看全部 -
霸道总裁的员工:类
我们上一小节中介绍了面向对象的思想,这一小节开始,我们来具体看看在 C++ 中应该如何实现面向对象。
类
类,是 C++ 实现面向对象最基础的部分。类其实和之前学过的结构体十分相似,你可以认为类是结构体的升级版。之后的学习中你会更加理解类。现在,我们只简单得来介绍一下类。
类的申明
在 C++ 中,可以用下面的代码申明一个员工类:
class Staff { };
可以像使用结构体一样使用这个类:
#include <stdio.h> class Staff { }; int main(int argc,char **argv) { Staff st; return 0; }
分文件编程
我们在此之前都是把代码放到一个文件里,但是这样在实际工程中肯定是不行的,我们不可能把所有的代码都写到一个文件夹里面。而在 C++ 中我们就常常把类定义到不同的文件里面,把每个类都独立起来,这样代码的耦合性就会降低,方便维护。
在 C++ 中,我们可以把一个类写到两个文件里面,一个是后缀为 .h 或者 .hpp 的头文件,一个是后缀为 .cpp 的实现文件。我们先在开发环境里新建一个类。输入类名是 Staff。
可以看到 VS 为我们创建类两个文件,Staff.h 和 Staff.cpp。Staff.h 文件为定义,Staff.cpp 为实现。
在分了文件之后,我们想要在 main 函数中引用这个类,就需要使用 #include “Staff.h” 将头文件引入进来。
实例化
在新建了一个类之后,我们就可以根据这个类产生对象了。根据类产生对象的过程叫做实例化。这个过程就像是公司招聘员工一样,幸运的是,我们作为程序的老板,并不需要和现实中一样去张贴招聘启示。在 C++ 中,“招聘“员工,只需要用以下的代码就可以了。
#include "Staff.h" int main(int argc,char **argv) { // 我们就这样实例化了三个员工 Staff st1; Staff st2; Staff st3; return 0; }
这样分配,我们将这三个“员工”分配到了栈上,同样的,可以把他们分配到堆内存上面去。
new delete
要将对象分配到堆上,需要用到另外两个关键字,new 和 delete。new 用来分配对象,delete 用来删除对象。new 会返回一个指针,在使用完毕后,要通过 delete 把这个指针指向的地址释放掉。
#include "Staff.h" int main(int argc,char **argv) { // 我们就这样实例化了三个员工 Staff * st1 = new Staff(); Staff * st2 = new Staff(); Staff * st3 = new Staff(); // 记得释放 delete st1; delete st2; delete st3; return 0; }
查看全部 -
指针变量其实和普通变量没有什么区别,一个函数也是可以正常返回一个指针的。
char * func()
{
char * p = nullptr;
return p;
}
int main(int argc,char **argv)
{
return 0;
}但是我们需要思考的是,什么情况下我们要返回一个指针,返回指针的时候需要我们注意些什么?
通常情况下,我们是希望为函数外提供一片内存,例如,我们可以给函数外面提供一个数组。
int * func()
{
int arr[] = {1, 2, 3, 4};
return arr;
}但是这样写得话,程序会崩溃掉。原因是,arr 数组是一个局部变量,在 func 结束之后,其内存就被销毁掉了。此时在函数外面对其进行操作,自然会出问题。所以,要完成这类操作,我们需要把内存分配到堆内存上面。
int * func()
{
int * arr = (int *)malloc(4 * sizeof(int));
return arr;
}这样就没有问题了,当然,既然是分配在了堆内存上,就要记得手动销毁。
int main(int argc,char **argv)
{
int * p = func();
free(p);
return 0;
}查看全部 -
代码块中定义的变量被称之为局部变量。它们在其他函数的语句中是不可见的,也无法访问它们。
全局变量是在所有函数体的外部定义的,程序的所有部分都可以使用。全局变量不受作用域的影响,其生命周期一直到程序的结束。
int a = 2;
int main(int argc,char **argv)
{
return 0;
}静态变量受作用域的影响,其生命周期一直到程序的结束。
例如:
void func()
{
static int a = 0;
}我们可以在函数中申明一个静态变量。值得注意的是,这个变量的作用域虽然是在函数内,但是他并不会随着函数结束而被销毁,它会一直存在到程序的结束。
查看全部 -
指针可以指向一个数组,例如:
#include <stdio.h>
int main(int argc,char **argv)
{
int arr[] = {1, 2, 3, 4};
int * p = arr;
return 0;
}比较特殊的是,数组名就是一个指针,不过数组名是一个常量指针,不能做累加或者累减操作。
我们可以通过指针来访问数组元素:
*(p + 2)
同样,这句话等价于:
p[2]
查看全部 -
指针可以指向一个变量,例如:
#include <stdio.h>
int main(int argc,char **argv)
{
int a = 0;
int * p = &a;
return 0;
}如果想要通过指针操作变量,只需要使用解引用就可以了:
*p = 20;
查看全部 -

.与->的不同用法
查看全部 -

arr里面放的是数组内第一个元素的地址,等同于&arr【0】

查看全部 -
#include <stdio.h>
int main()
{
int i = 0x1122;
char * p = (char *)&i;
if (p[0] == 0x22 && p[1] == 0x11) {
printf("Little Endian\n");
}
else if (p[0] == 0x11 && p[1] == 0x22) {
printf("Big Endian\n");
}
}判断电脑是哪种字节序
查看全部 -
字节序,就是 大于一个字节类型的数据在内存中的存放顺序。
计算机硬件有两种储存数据的方式:大端字节序(big endian)和小端字节序(little endian)。
我们现在有一个整数是258。用16进制表示是0x0102,然后我们把这个整数拆分成两个字节,第一个字节为 0000 0001,第二个字节为 0000 0010。
如果在一个使用大端字节序的电脑上,这个整数会被这样存放:

如果一个使用小端字节序的电脑上,这个整数的高字节就会存放在高地址上:

现在大部分的机器,都采用了小端字节序。但是在 IO 方面,则大部分使用大端字节序。例如,你要使用网络发送一个 int 类型的变量,要先把 int 转换成大端字节序,然后通过网络发送。
大端字节序又被称之为网络细节序。
查看全部 -
~ 取反
^ 异或
<< 左移
>> 右移
查看全部 -
浮点数会有一定的精度范围,一旦小数点后面位数过多,超出范围,就有可能失去精度。
浮点数的存放复杂,运行起来速度也比较慢,如无必要,还是用整数方便快捷。
查看全部 -
复制一个员工:赋值构造函数
上一小节中,我们介绍了构造函数和析构函数。这一小节,我们来介绍一个特殊的构造函数。
先来看一个例程:
int main(int argc,char **argv) { Staff staffA; Staff staffB = staffA; return 0; }
我们先实例化了一个对象 staffA,然后又实例化了一个对象 staffB。希望 staffB 和 staffA 具有相同的内容,或者说希望 staffB 是 staffA 的副本,那么我们一般直接在实例化的时候进行赋值就可以了。
Staff staffB = staffA;
这样做了之后,C++ 会自动为我们拷贝 staffA 中的成员变量到 staffB 的成员变量中,但是这种自动拷贝机制在某些情况下无法完成我们想要的动作,甚至有可能会出错。我们来具体看一下。
在 Staff 类中添加一些内容:
Staff.hpp
#include <string>
class Staff {
public:
Staff(std::string _name, int _age);
~Staff();
public:
std::string name;
int age;
char * mem = nullptr; };
Staff.cpp
#include "Staff.hpp"
#include <stdio.h>
Staff::Staff(std::string _name, int _age) {
mem = (char *)malloc(20);
name = _name;
age = _age;
printf("构造函数被调用\n");
}
Staff::~Staff() {
if(mem != nullptr){ free(mem); mem = nullptr; }
printf("析构函数被调用\n");
}
在上面的代码中,在类中定义了一个指针,在构造函数中,通过 malloc 函数分配了一个 20 字节大小的堆内存,然后将这片堆内存的首地址赋值给了这个指针。在析构函数中,我们将这片堆内存释放掉。
这个时候,我们再进行一开始的操作:
Staff staffB = staffA;
先来看看 staffA 在实例化之后的内存布局:

mem 指针指向了一片 20 个字节大小的堆内存。
这个时候,再来看看执行了Staff staffB = staffA;之后,staffB 的内存布局会怎么样:

可以看到,在 C++ 默认的复制模式之下,两个对象中的 mem 指针指向了同一片内存。因为 C++ 默认的复制模式只会简单得把成员的值进行复制,面对这个 mem 指针,他只会把指针的值进行拷贝,最后的结果就是 mem 指针指向了同一片内存。
这种拷贝方式,被称之为浅拷贝。
赋值构造函数
Staff staffB = staffA;
当我们使用这种方式实例化对象的时候,并不会调用普通构造函数,而是会调用一个特殊的构造函数,被称之为赋值构造函数或者拷贝构造函数。如果我们没有写,那么就会按照浅拷贝的方式来进行复制。一个拷贝构造函数看起来就像下面这样:
Staff(const Staff & staff);
这个函数中只有一个参数 staff,表示要拷贝的对象,在我们的例子中,就是 staffA。(const 和 & 我们在后续的课程中会具体讲解)
那么我们来完整的编写一下这个函数
Staff.h
#include <string>
class Staff {
public:
Staff(std::string _name, int _age);
Staff(const Staff & staff);
~Staff();
public:
std::string name;
int age;
char * mem = nullptr; };
Staff.cpp
#include "Staff.hpp"
#include <stdio.h>
Staff::Staff(std::string _name, int _age) {
mem = (char *)malloc(20);
name = _name;
age = _age;
printf("构造函数被调用\n");
}
Staff::Staff(const Staff & staff) {
name = staff.name;
age = staff.age;
mem = (char *)malloc(20);
memcpy(mem, staff.mem, 20);
}
Staff::~Staff() {
if(mem != nullptr){ free(mem); mem = nullptr; }
printf("析构函数被调用\n");
}
查看全部
举报



