Skip to content

Commit 89e6751

Browse files
committed
update
1 parent 6a5b3ee commit 89e6751

File tree

3 files changed

+139
-5
lines changed

3 files changed

+139
-5
lines changed

C++入门/1.C++基础.md

Lines changed: 46 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -197,18 +197,35 @@ void func() {
197197
`extern int globe;`
198198
告诉编译器变量存在哪里。
199199

200+
###### extern c
201+
202+
有时候会经常在C和C++中进行相互调用。
203+
204+
在实际项目中,如果一个C++工程需要调用C语言编写的静态库或者动态库,那么可以直接调用吗?
205+
206+
显然是不可以直接调用的,对于C++代码,因为C++有自己的函数名修饰规则,在编译和汇编阶段形成符号表的时候,编译器是按照C++自己的函数名修饰规则来表示符号的,而对于C的代码,也有自己的函数名修饰规则,在编译和汇编阶段,编译器是按照C的函数名修饰规则来形成符号表的,那么在链接阶段,由于符号表中表示同一函数的符号名都是不同的,链接器无法找到相应函数的地址,也就无法进行符号表的合并和重定位,就会产生链接错误。
207+
208+
同理,一个C工程要调用C++语言编写的静态库或者动态库,也是不可以直接调用的。
209+
210+
那么一个C++工程如何调用C语言编写的代码呢?一个C工程又如何调用C++语言编写的代码呢?
211+
212+
这实际上是一个代码可移植问题,这里就需要用到条件编译和extern "C"了。
213+
214+
C语言中并没有重载和类这些特性,故并不像C++那样print(int i),会被编译为_print_int,而是直接编译为_print等。因此如果直接在C++中调用C的函数会失败,因为连接是调用C中的 print(3)时,它会去找_print_int(3)。因此extern "C"的作用就体现出来了。
215+
200216

201217
如果在C++中编写一个程序需要用到C的库,那该怎么办呢?如果这样声明一个C函数:
202218
```c
203219
float f(int a, char b);
204220
```
205-
C++的编译器就会将这个名字变成像_f_int_char之类的东西以支持函数重载(和类型安全连接)。然而,C编译器编译的库一般不做这样的转换,所以它的内部名为_f。这样,连接器无法解释C++对f()的调用。
221+
C++的编译器就会将这个名字变成像_f_int_char之类的东西以支持函数重载(和类型安全连接),而C中是不支持函数重载的,所以不会修改函数的名字。然而,C编译器编译的库一般不做这样的转换,所以它的内部名为_f。这样,连接器无法解释C++对f()的调用。
206222
C++中提供了一个替代连接说明(alternate linkage specification),它是通过重载extern关键字来实现的。
207223
extern后跟一个字符串来指定想声明的函数的连接类型,后面是函数声明。
208224
```c++
209225
extern "C" float f(int a, char b);
210226
```
211-
这就告诉编译器f()是C连接,这样就不会转换函数名。标准的连接类型指定符有"C"和"C++"两种,如果有一组替代连接的声明,可以把它们放在花括号内:
227+
这就告诉编译器f()是C连接,这样就不会转换函数名。
228+
标准的连接类型指定符有"C"和"C++"两种,如果有一组替代连接的声明,可以把它们放在花括号内:
212229
```c++
213230
extern "C" {
214231
float f(int a, char b);
@@ -577,11 +594,11 @@ extern const int bufSize; // 与file_1.cc中定义的bufSize是同一个
577594
file_1.h头文件中的声明也由extern做了限定,其作用是指明bufSize并非本文件所独有,它的定义将在别处出现。
578595

579596
#### const引用
580-
可以把引用绑定到const对象上,就像绑定到其他对象上一样,我们称之为对常量的引用(reference to const)。
581-
与普通引用不同的是,对常量的引用不能被用作修改它所绑定的对象。
597+
const引用是指向const对象的引用。
582598
```c++
583599
const int ci = 1024;
584600
const int &r1 = ci; // 正确:引用及其对应的对象都是常量
601+
int &ref2 = ci; // 错误:const对象的引用必须是const引用
585602
void Start::show() const {...}
586603
```
587604
这里的const表示`const Start* this`,而this指向调用的对象。
@@ -598,6 +615,18 @@ const Stock& Stock::topval(const Stock& s) const {
598615
该方法返回对this或s的引用。因为this和s都被声明为const,所以函数不能对他们进行修改,这意味着返回的引用也必须被声明为const。
599616
注意,如果函数将参数声明为指向const的引用或指针,则不能将该参数传递给另一个函数,除非后者也确保了参数不会被修改。
600617

618+
619+
##### const修饰方法或对象
620+
const修改函数时可以构成重载。
621+
```c++
622+
void dis() const;
623+
624+
void dis();
625+
```
626+
- const修饰函数,是从函数的层面,该函数内部不能修改成员数据。
627+
- const修饰对象,是从对象层面不修改数据,只能调用const成员函数。const对象只能调用const成员函数。非const成员对象,优先调用非const成员函数,若无,则可调用const成员函数。
628+
629+
601630
##### 指针和const
602631
与引用一样,也可以令指针指向常量或非常量。
603632
指向常量的指针(pointer to const)不能用于改变其所指对象的值。要想存放常量对象的地址,只能使用指向常量的指针:
@@ -633,6 +662,9 @@ const int *p = &a; // 常量指针:指针的指向可以修改,但是指针
633662
*p = 20; // 错误,指针指向的值不可以改
634663
p = &b; // 正确,指针指向可以改
635664
```
665+
一个简单的记法就是:
666+
`const int *p`,那const就是修饰的 `*p`,也就是指针指向的值是不能修改的。
667+
636668

637669
###### 指针常量(const修饰常量)
638670
```c++
@@ -641,6 +673,9 @@ int * const p = &a; //指针的指向不可以改,但是指针指向的值可
641673
p = &b; // 错误,指针指向不可以改
642674
```
643675

676+
一个简单的记法就是:
677+
`int * const p = &a;`,那const修饰的是`p`,也就是指针是不能修改的。那p就是一个常量,常量是必须要初始化的。
678+
644679
###### const即修饰指针,又修饰常量
645680
```c++
646681
const int * const p = &b; // 指针的指向和指针指向的值都不可以改
@@ -966,7 +1001,7 @@ class Box {
9661001
static int height;
9671002
static int volume();
9681003
};
969-
1004+
// static的属性不能在类内初始化,必须在类外初始化
9701005
int Box::length = 5;
9711006
int Box::width = 3;
9721007
int Box::height = 2;
@@ -979,6 +1014,12 @@ int main() {
9791014
return 0;
9801015
}
9811016
```
1017+
static在类内部用来实现类对象间的数据共享。在生成对象的时候,普通数据成员才有空间。
1018+
而static成员在类指明的时候就已经开辟了空间。
1019+
static数据成员,即属于类,也属于对象,但终归还是属于类。
1020+
- static变量初始化
1021+
类内定义,类外初始化。 type类名::变量名=初始值
1022+
9821023
9831024
##### 强制类型转换
9841025
```c++

C++入门/2.类.md

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,92 @@ int main() {
175175
析构函数完成清理工作,因此实际上很有用。
176176
例如,如果构造函数使用new来分配内存,则析构函数将使用delete来释放这些内存。 上面Stock的构造函数如果没有使用new,因此析构函数实际上没有需要完成的任务。在这种情况下,只需让编译器生成一个什么都不做的析构函数即可。
177177
178+
### 继承
179+
- Student.hpp
180+
```c++
181+
#ifndef Student_hpp
182+
#define Student_hpp
183+
184+
#include <stdio.h>
185+
#include <iostream>
186+
using namespace std;
187+
188+
class Student {
189+
public:
190+
Student(string sn, int ia, float fs);
191+
void dis();
192+
private:
193+
string name;
194+
int age;
195+
float sscore;
196+
};
197+
198+
199+
#endif /* Student_hpp */
200+
```
201+
- Student.cpp
202+
```c++
203+
#include "Student.hpp"
204+
205+
Student::Student(string sn, int ia, float fs)
206+
:name(sn), age(ia), sscore(fs) {
207+
208+
}
209+
210+
void Student::dis() {
211+
cout << "name" << name << endl;
212+
}
213+
```
214+
- Graduate.hpp
215+
```c++
216+
#ifndef Graduate_hpp
217+
#define Graduate_hpp
218+
219+
#include <stdio.h>
220+
#include "Student.hpp"
221+
using namespace std;
222+
class Graduate : public Student {
223+
public:
224+
Graduate(string sn, int ia, float fs, double ds);
225+
void print();
226+
private:
227+
double salary;
228+
};
229+
230+
#endif /* Graduate_hpp */
231+
```
232+
- Graduate.cpp
233+
```c++
234+
#include "Graduate.hpp"
235+
236+
// 构造函数中调用父类构造函数,对于特有的属性调用自己的初始化方法
237+
Graduate::Graduate(string sn, int ia, float fs, double ds)
238+
: Student(sn, ia, fs), salary(ds){
239+
240+
}
241+
242+
void Graduate::print() {
243+
dis();
244+
cout << "salary" << salary << endl;
245+
}
246+
```
247+
- main.cpp
248+
```c++
249+
#include <iostream>
250+
#include "Student.hpp"
251+
#include "Graduate.hpp"
252+
using namespace std;
253+
254+
int main(int argc, const char * argv[]) {
255+
Student s("zhaosi", 32, 120);
256+
s.dis();
257+
Graduate g("nengge", 35, 105, 200.0);
258+
g.print();
259+
return 0;
260+
}
261+
```
262+
263+
178264
### 结构体
179265

180266
联合类型(union)与结构类型(struct)无论是在定义方法还是成员存取上都十分相似,但结构类型所定义的每个成员拥有各自的内存空间,而联合却是共享内存空间。联合变量内的各个成员以同一个内存区块来存储数据,并以占最大内存空间的成员作为联合类型的内存空间大小。
@@ -192,6 +278,9 @@ int main() {
192278
抽象基类包含了最少一个或多个纯虚拟函数,所以当派生类继承了抽象基类之后,必须在派生类中“重新定义”(override,或者覆盖)及“实现”(implement)所继承的纯虚拟函数。
193279

194280

281+
含有纯虚函数的类就是Java中的接口。是不能实例化的。
282+
含有虚函数的类,析构函数也应该声明为虚函数。在delete父类指针的时候,会调用子类的析构函数,实现完整析构。
283+
195284
实现多态性:
196285
在C++语言中实现多态性,需在基类的函数前加上virtual关键字,在派生类中重写该函数,运行时将会根据对象的实际类型来调用相应的函数。如果对象类型是派生类,就调用派生类的函数;如果对象类型是基类,就调用基类的函数。
197286

C++入门/4.动态内存.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,10 @@ C++确保空指针不会指向有效的数据,因此它常被用于表示运
5151
int* ps = new int;
5252
...
5353
delete ps;
54+
55+
56+
int *p2 = new int[10];
57+
delete[] p2; // 数组的delete必须带[]
5458
```
5559

5660
##### 使用new来创建动态数据

0 commit comments

Comments
 (0)