类的入门
类的声明与定义
类的声明主要用来告诉编译器某个类的名字及其成员的类型和布局,声明通常位于头文件中。声明需要分号在语句的背后。比如我声明一个叫作Jojo
的类,他有一个string
类型的名字,一个int
,以及两个函数,一个没有返回值,一个返回string
,我会这么编写这个声明:
1 | //jojo.h |
然后在一个cpp
文件中实现这个类的一些函数:
1 |
|
C++的分号规则
- 宏定义没有分号,他不是一个普通的C++语句。
- 函数定义没有分号,定义是一个代码块,因此不需要在定义的末尾加上分号。
- 函数声明末尾需要有一个分号。
- 类的声明和定义结束后都需要加一个分号,类的成员函数声明和常规函数一样需要加上分号,类的成员函数实现和常规函数一样不需要分号。
关于返回值
写久了Python对于返回值的类型可能就不太习惯了。
在C++中,函数的声明和定义都需要指定返回值类型,函数的返回值类型是函数签名的一部分,编译器需要根据它来确定函数的用途和返回值的类型。但是在现代C++(C++11及以后)中,可以使用auto
关键字让编译器根据返回值类型自动推导,这样在定义时不需要显式指定返回值类型。
1 | auto add(int a, int b) -> int { |
关于生成类对象
我们可以用两种方法生成类对象,一种是把类对象生成在栈上,另一种是把类对象生成在堆上。
1 | Jojo my_jojo = Jojo(3); |
另一种是把类对象生成在栈上:
1 | Jojo* my_jojo_pointer = new Jojo(3); |
然后使用这个对象的方法也不一样,
1 | std::cout << my_jojo.get_value() << std::endl; |
他们两个的大小也不一样:
1 | std::cout << sizeof(my_jojo) << std::endl; |
这两个的区别是,通过指针来生成对象时,是通过new
关键字来把对象动态地分配在堆内存(heap)上,其实写这些编译语言本质上就是控制内存吗。直接生成对象,就相当于本身就开辟了这个对象的内存。而生成一个对象的指针,这个内存就不会有人来帮我们开辟哈。这种通过new
方式来生成的对象的声明周期需要由我们来手动管理,而且必须调用delete
来释放内存。如果忘记释放,就会导致内存泄漏。通过指针来访问成员函数使用->
语句。
这种方式的优点是可以用于创建在运行时数量不固定的对象。
直接定义对象分配在栈内存(stack)中,由编译器自动管理,使用.
来直接访问成员函数,但这种方式不适合在运行时创建数量不确定的对象。
生成类对象数组
和上面生成对象一样,生成类对象数组也有两种方式。一种是通过指针来开辟在堆内存上:
1 | Jojo* my_class_list = new Jojo[3] {Jojo(1), Jojo(2), Jojo(3)}; |
另一种是直接栈分配定义对象:
1 | Jojo my_class_list[3] = {Jojo(1), Jojo(2), Jojo(3)}; |
大小也不一样哈,my_class_list
是一个8字节的指针,my_class_list
直接就是\(3\times40=120\)字节的对象。
但是使用第几个对象的方法还是一样的。