1. 基本原理知识
函数重载:在相同作用域中的多个函数,具有相同的名字而形参表不同。
不能仅仅基于不同的返回类型而实现函数重载。返回值是不影响函数签名的。
C++函数重载底层实现原理是C++利用name mangling(倾轧)技术,来改名函数名,区分参数不同的同名函数。
编译器通过函数名和其参数类型识别重载函数。为了保证类型安全的连接(type-safe linkage),编译器用参数个数和参数类型对每一个函数标识符进行专门编码,这个过程有时称为“名字改编”(name mangling)或“名字修饰”(name decoration)。类型安全的连接使得程序能够调用合适的重载函数并保证了参数传递的一致性。编译器能够检测到并报告连接错误。
objdump -t 查看符号表
_Z开头的文件;(重载文件)
2. gcc编译
不能压榨,所以不能进行重载;
- test.c
int func(int a){};
- 编译
gcc -c test.c -o test.o
- 查看符号
objdump -t test.o
函数名 | 名字改编 |
int func(int a); | func |
3. g++编译
- test.cpp
int func(int a){};
int func(int a,int b){};
- 编译
g++ -c test.cpp -o test.o
- 查看符号
objdump -t test.o
函数名 | 名字改编 |
int func(int a); | _Z4funci |
int func(int a,int b); | _Z4funcii |
_Z是规定前缀,4是函数名的字符个数,i是参数列表类型i的首字母
注意:不同系统和编译器的命名倾轧方式是有所不同的。
4. 命名反倾轧
名字改编转化成函数名
使用 c++filt 命令可以很容易把名字改编转换成函数名。
例如:
c++filt _Z4funci
5. 实例
(1)编写代码
#include <stdio.h>
void Func(int,int){}
void Func(int){}
void Func(){}
class Simple{
int n;
public:
Simple(int n):n(n){}
Simple(const Simple&){}
Simple& operator=(const Simple&){
return *this;
}
~Simple(){}
void Func(){}
};
int main(){
Simple s(10);
Simple s1 = s;
s1 = s;
// printf("Func=%p\n",&Func);
printf("main=%p\n",&main);
}
(2)编写代码
- 编译
gcc -c test.c -o test.o
- 查看符号
objdump -t test.o
- 反编译
c++filt _Z4funci
6. 禁用命名倾轧
C++命名倾轧的函数是无法被C语言调用的。C++的函数必须是没有倾轧的才能调用。
使用声明extern "C"的函数会禁止命名倾轧,这样C++的函数就可以被C语言调用。
编译出来的函数名仅仅保留名字,与gcc编译的结果一样;
(1)这样的函数不支持重载;
extern "C" int func(int a){
return a*10;
}
#if(0)
int func(){
return 0;
}
#endif
- 完整案例
#include <iostream>
using namespace std;
extern "C" int func(int a){
return a*10;
}
/*使用声明`extern "C"`的函数会禁止命名倾轧,这样C++的函数就可以被C语言调用。
编译出来的函数名仅仅保留名字,与gcc编译的结果一样;
(1)这样的函数不支持重载;*/
#if(0)
int func(){
return 0;
}
#endif
int main()
{
int n = func(20);
return 0;
}
6. 禁用命名倾轧
C++命名倾轧的函数是无法被C语言调用的。C++的函数必须是没有倾轧的才能调用。
使用声明extern "C"的函数会禁止命名倾轧,这样C++的函数就可以被C语言调用。
编译出来的函数名仅仅保留名字,与gcc编译的结果一样;
(1)这样的函数不支持重载;
extern "C" int func(int a){
return a*10;
}
#if(0)
int func(){
return 0;
}
#endif
- 完整案例
#include <iostream>
using namespace std;
extern "C" int func(int a){
return a*10;
}
/*使用声明`extern "C"`的函数会禁止命名倾轧,这样C++的函数就可以被C语言调用。
编译出来的函数名仅仅保留名字,与gcc编译的结果一样;
(1)这样的函数不支持重载;*/
#if(0)
int func(){
return 0;
}
#endif
int main()
{
int n = func(20);
return 0;
}
7. c/c++的交叉编译
7. 1 c++环境编译c文件
情况一:直接编译c文件
g++ xxx.c //直接编译c文件
情况二:c++里面使用c语言的语法
(1)添加需要的头文件;
(2)添加需要的函数,功能相同的话,选择c++里面的函数,避免矛盾;
7. 2 c环境编译c++文件
步骤一:头文件(array.h)需要注意:
(1)头文件里面包含cpp文件里面的函数声明,
(2)但是不能包含extern “C”,不识别;
int sum(int* arr, int n);
void reverse(int* arr, int n);
int max_element(int* arr,int n);
步骤二:(array.cpp)需要注意:
(1)使用extern “C”实现函数的实现;
//(1)使用extern “C”实现函数的实现;
extern "C" int sum(int* arr, int n){
return accumulate(arr,arr+n,0);
}
(2)不能使用流运算符和其头文件;
//(2)不能使用流运算符和其头文件;
//#include <iostream>
步骤三:测试函数(main.c)需要注意:
只能在main.c里面出现main()函数,多个main()函数会报错;
步骤四:编译(编译二进制文件),但是code::blocks不用这样,直接编译;
g++ -c array.cpp
gcc sum_test.c sum.o
- array.h
/*
步骤一:头文件(array.h)需要注意:
(1)头文件里面包含cpp文件里面的函数声明,
(2)但是不能包含extern “C”,不识别;
*/
#ifndef ARRAY_H_INCLUDED
#define ARRAY_H_INCLUDED
int sum(int* arr, int n);
void reverse(int* arr, int n);
int max_element(int* arr,int n);
#endif // ARRAY_H_INCLUDED
(2)array.cpp
//#include <iostream>
//流运算符不能使用在array.cpp文件中;
//(2)不能使用流运算符和其头文件;
//#include <iostream>
#include <algorithm>
#include <numeric>
using namespace std;
//(1)使用extern “C”实现函数的实现;
extern "C" int sum(int* arr, int n){
return accumulate(arr,arr+n,0);
}
extern "C" void reverse(int* arr, int n){
reverse(arr,arr+n);
}
extern "C" int max_element(int* arr, int n){
return *max_element(arr,arr+n);
}
#if(0)
int main(){
int arr[] = {1,3,5,2,7};
cout << sum(arr,5) << endl;
for_each(arr,arr+5,[](int n){
cout << n << endl;});
}
#endif
(3)main.c
#include <stdio.h>
#include <stdlib.h>
#include "array.h"
//测试函数(main.c)需要注意:只能在main.c里面出现main()函数,多个main()函数会报错;
void Print(int* arr, int n){
for(int i = 0;i < n;++i){
printf("%d\t", arr[i]);
}
printf("\n");
}
int main()
{
int arr[] = {1,4,2,6,9,3};
printf("%d\n", sum(arr,6));
printf("%d\n", max_element(arr,6));
Print(arr,6);
reverse(arr,6);
Print(arr,6);
return 0;
}
7. 3 c++和c之间都能使用头文件的方式
(1)将头文件修改为c_plus_plus和非c++类型;
#ifdef __cplusplus
extern "C"{
#endif // __cplusplus
int sum(int* arr, int n);
void reverse(int* arr, int n);
int max_element(int* arr,int n);
#ifdef __cplusplus
}
#endif
(2)声明中出现extern “C”,则实现中不用出现
(3)注意:#include
流运算符不能使用在array.cpp文件中;
- 完整代码
main.c ,main.cpp 二选一进行测试 - array.h
#ifndef ARRAY_H_INCLUDED
#define ARRAY_H_INCLUDED
//(1)将头文件修改为c_plus_plus和非c++类型;
#ifdef __cplusplus
extern "C"{
#endif // __cplusplus
int sum(int* arr, int n);
void reverse(int* arr, int n);
int max_element(int* arr,int n);
#ifdef __cplusplus
}
#endif
#endif // ARRAY_H_INCLUDED
- array.cpp
#include <iostream>
//流运算符不能使用在array.cpp文件中;
//#include <iostream>
#include <algorithm>
#include <numeric>
#include "array.h"
using namespace std;
int sum(int* arr, int n){
return accumulate(arr,arr+n,0);
}
void reverse(int* arr, int n){
reverse(arr,arr+n);
}
int max_element(int* arr, int n){
return *max_element(arr,arr+n);
}
#if(0)
int main(){
int arr[] = {1,3,5,2,7};
cout << sum(arr,5) << endl;
for_each(arr,arr+5,[](int n){
cout << n << endl;});
}
#endif
- main.c
#include <stdio.h>
#include <stdlib.h>
#include "array.h"
//测试函数(main.c)需要注意:只能在main.c里面出现main()函数,多个main()函数会报错;
void Print(int* arr, int n){
for(int i = 0;i < n;++i){
printf("%d\t", arr[i]);
}
printf("\n");
}
int main()
{
int arr[] = {1,4,2,6,9,3};
printf("%d\n", sum(arr,6));
printf("%d\n", max_element(arr,6));
Print(arr,6);
reverse(arr,6);
Print(arr,6);
return 0;
}
- main.cpp
#include <iostream>
#include <algorithm>
#include <numeric>
#include "array.h"
using namespace std;
int main(){
int arr[] = {1,3,5,2,7};
cout << sum(arr,5) << endl;
for_each(arr,arr+5,[](int n){
cout << n << endl;});
}
7. 4 c可以使用c++定义的独特的方便的函数(接口)
(原理)(1)即编译的时候g++生成_Z开头的文件;
gcc生成和extern “C”生成Func文件;
(2)调用的时候,c调用Func;
c++调用_Z开头的文件;
- 完整案例
(1)array.h
#ifndef __VECTOR_H
#define __VECTOR_H
namespace miniSTL{
template<typename T>
class vector{
public:
typedef T value_type;
typedef T* pointer;
typedef T& reference;
typedef const T& const_reference;
typedef int size_type;
typedef pointer iterator;
private:
/*
pointer _data; // 地址
size_type _size; // 元素个数
size_type _capacity; // 容量大小
*/
//
pointer _start; // _start == _data
pointer _finish; // _size == _finish - _start;
pointer _endOfStorage; // _capacity == _endOfStorage - _start
public:
vector():_start(NULL),_finish(NULL),_endOfStorage(NULL){}
void push_back(const_reference val){
if(NULL == _start){
_start = new value_type[1];
_finish = _endOfStorage = _start+1;
_start[0] = val;
return;
}
int last = size();
if( _finish==_endOfStorage){
int new_capacity = capacity()*2;
pointer tmp = new value_type[new_capacity];
for(int i=0;i<last;++i){
tmp[i] = _start[i];
}
delete [] _start;
_start = tmp;
_endOfStorage = _start+new_capacity;
}
_start[last] = val;
_finish = _start+last+1;
}
void insert(iterator it,value_type val){
// 此处省略申请空间的处理
iterator last = end();
while(last!=it){
*last = *(last-1);
last = last-1;
}
*it = val;
++_finish;
}
size_type size()const{return _finish-_start;}
size_type capacity()const{return _endOfStorage-_start;}
reference operator[](int index){ return _start[index];}
reference at(int index){ return _start[index];}
const_reference operator[](int index)const{ return _start[index];}
// 迭代器
iterator begin(){return _start;}
iterator end(){return _finish;}
};
}
#endif // __VECTOR_H
- array.cpp
#include <algorithm>
#include <numeric>
//#include "vector.h"
#include <vector>
#include "array.h"
using namespace std;
//using namespace miniSTL;
int sum(int* arr,int n){
return accumulate(arr,arr+n,0);
}
void reverse(int* arr,int n){
reverse(arr,arr+n);
}
int max_element(int* arr,int n){
return max_element(arr,arr+n) - arr;
}
void* seq_create(){
return new vector<int>();
}
void seq_destroy(void* seq){
delete reinterpret_cast<vector<int>*>(seq);
}
void seq_append(void* seq,int val){
reinterpret_cast<vector<int>*>(seq)->push_back(val);
}
void seq_prepend(void* seq,int val){
vector<int>* p = reinterpret_cast<vector<int>*>(seq);
p->insert(p->begin(),val);
}
int seq_size(void* seq){
return reinterpret_cast<vector<int>*>(seq)->size();
}
int seq_get(void* seq,int index){
return reinterpret_cast<vector<int>*>(seq)->at(index);
}
- main.c
#include "array.h"
#include <stdio.h>
void Print(int* arr,int n){
for(int i=0;i<n;++i){
printf("%d ",arr[i]);
}
printf("\n");
}
int main(){
int arr[] = {1,4,2,6,9,3};
printf("%d\n",sum(arr,6));
printf("%d\n",max_element(arr,6));
Print(arr,6);
reverse(arr,6);
Print(arr,6);
void* seq = seq_create();
seq_append(seq,1);
seq_append(seq,2);
seq_append(seq,3);
for(int i=0;i<seq_size(seq);++i){
printf("%d ",seq_get(seq,i));
}
printf("\n");
seq_prepend(seq,0);
seq_prepend(seq,-1);
seq_prepend(seq,-2);
for(int i=0;i<seq_size(seq);++i){
printf("%d ",seq_get(seq,i));
}
printf("\n");
seq_destroy(seq);
}
8. 空类
8.1. 空类的大小是多少?
1个字节
8.2. 编译器会给空类自动生成几个成员函数?
- 默认(缺省的)构造函数
- 析构函数
- 拷贝构造函数
- 赋值运算符重载函数
- 两个取址运算符重载函数
class Empty {
public:
Empty(){} //缺省构造函数
Empty(const Empty &rhs){} //拷贝构造函数
~Empty(){} //析构函数
Empty& operator=(const Empty &rhs){} //赋值运算符重载函数
Empty* operator&(){} //取址运算符重载函数
const Empty* operator&() const{} //取址运算符重载函数(const版本)
};
调用时机
Empty *e = new Empty(); //缺省构造函数
delete e; //析构函数
Empty e1; //缺省构造函数
Empty e2(e1); //拷贝构造函数
Empty e3 = e1; //拷贝构造函数
e2 = e1; //赋值运算符
Empty *pe1 = &e1; //取址运算符重载函数(非const)
const Empty *pe2 = &e2; //取址运算符重载函数(const)