C调用C++类成员函数--实例

简介: C与C++调用问题原因主要在于C编译器和C++编译器的不同。C是过程式语言,C编译器编译后,函数在符号库中就是函数名,没有其他任何附加信息。而C++是对象式语言,支持函数重载,C++编译器编译后,在符号库中的名字包含了函数名、函数参数类型和函数返回类型等。因此,当两者混合编译时,会相互找不到对象。


1. 问题成因

C与C++调用问题原因主要在于C编译器和C++编译器的不同。C是过程式语言,C编译器编译后,函数在符号库中就是函数名,没有其他任何附加信息。而C++是对象式语言,支持函数重载,C++编译器编译后,在符号库中的名字包含了函数名、函数参数类型和函数返回类型等。因此,当两者混合编译时,会相互找不到对象。

2. 反汇编证明

我们通过反汇编来证明上述问题,请看下面的例子:

#include "stdio.h"
int funP(int a)
{
    return 0;
}
int main()
{
    int a = 0,ret;
    ret = funP(a);
    return 0;
}

image.gif

对这一段小程序test.c,我们分别使用gcc和g++两种工具进行编译:

gcc test.c -o testc
g++ test.c -o test+

image.gif

对可执行程序进行反汇编

image.gif编辑

可以看到gcc编译的testc反汇编出来函数名为就是funP,而g++编译的test+反汇编出来函数名为_Z4funPi,带上了返回类型和函数参数类型。两者的不一致导致C与C++相互调用无法找到对应函数。

在C和C++混合编程中,常见的是C++调用C,这种是比较简单的,一个extern “C”{}即可解决问题。而C调用C++则是使用相对较少的,我们通过一个实例来说明C如何调用C++类成员函数。

3.C++程序

3.1 add.h

#ifndef ADD_H
#define ADD_H
class addCode
{
public:
  addCode();
  ~addCode();
  void setNum(int a);   
  int getNum();
private:
  int m_a;  
};
#endif

image.gif

3.2 add.cpp

#include <stdio.h>
#include "add.h"
addCode::addCode()
{
  m_a = 0;
}
addCode::~addCode()
{
}
void addCode::setNum(int a)
{
  a++;
  printf("c++ a = %d\n",a);
  m_a = a;
}
int addCode::getNum()
{
  return m_a;
}

image.gif

4. 中间封装程序

通过中间封装程序,实现C与C++之间的转换,中间封装头文件提供给C和C++程序来调用。

4.1 myadd.h

extern “C”{ } 告诉C++编译器以C的规则来链接函数,以便C编译器之后在对象文件中找到正确的符号。#ifdef _ucplusplus 是因为C编译器不知道关键字extern。

#ifndef MYADD_H
#define MYADD_H
#ifdef __cplusplus
extern "C"{
#endif
typedef struct addCode addCode;
addCode* newAddCode();
void interSetNum(addCode *v,int a);
int interGetNum(addCode *v);
#ifdef __cplusplus
}
#endif
#endif

image.gif

4.2 myadd.cpp

#include "add.h"
#include "myadd.h"
extern "C"{
  addCode* newAddCode(){
    return new addCode();
  }
  void interSetNum(addCode *v,int a)
  {
    v->setNum(a);
  }
  int interGetNum(addCode *v)
  {
    return v->getNum();
  }
}

image.gif

5. C主程序 main.c

#include <stdio.h>
#include "myadd.h"
int main(void)
{
  int a = 1;
  struct addCode* v = newAddCode();
    //传递参数给C++
  interSetNum(v,a);
  printf("main a = %d\n",a);
    //获取C++中参数值
  a = interGetNum(v);
  printf("main get a = %d\n",a);
  return 0;
}

image.gif

6. 编译执行

为了体验过程,我们不再编写makefile,依次使用如下命令行进行编译:

g++ -c add.cpp -o add.o
g++ -c myadd.cpp -o myadd.o
gcc -c main.c -o main.o
g++ add.o myadd.o main.o -o test

image.gif

编译完成,执行程序,结果如下:

image.gif编辑

通过上述例子,我们实现了C对C++的调用,大家可以参考。


相关文章
|
1天前
|
存储 编译器 Linux
【c++】类和对象(上)(类的定义格式、访问限定符、类域、类的实例化、对象的内存大小、this指针)
本文介绍了C++中的类和对象,包括类的概念、定义格式、访问限定符、类域、对象的创建及内存大小、以及this指针。通过示例代码详细解释了类的定义、成员函数和成员变量的作用,以及如何使用访问限定符控制成员的访问权限。此外,还讨论了对象的内存分配规则和this指针的使用场景,帮助读者深入理解面向对象编程的核心概念。
9 4
|
24天前
|
存储 编译器 对象存储
【C++打怪之路Lv5】-- 类和对象(下)
【C++打怪之路Lv5】-- 类和对象(下)
22 4
|
24天前
|
编译器 C语言 C++
【C++打怪之路Lv4】-- 类和对象(中)
【C++打怪之路Lv4】-- 类和对象(中)
20 4
|
23天前
|
存储 安全 C++
【C++打怪之路Lv8】-- string类
【C++打怪之路Lv8】-- string类
18 1
|
1月前
|
存储 编译器 C++
【C++类和对象(下)】——我与C++的不解之缘(五)
【C++类和对象(下)】——我与C++的不解之缘(五)
|
1月前
|
编译器 C++
【C++类和对象(中)】—— 我与C++的不解之缘(四)
【C++类和对象(中)】—— 我与C++的不解之缘(四)
|
1月前
|
编译器 C语言 C++
C++入门3——类与对象2-2(类的6个默认成员函数)
C++入门3——类与对象2-2(类的6个默认成员函数)
23 3
|
1月前
|
C++
C++番外篇——对于继承中子类与父类对象同时定义其析构顺序的探究
C++番外篇——对于继承中子类与父类对象同时定义其析构顺序的探究
51 1
|
1月前
|
编译器 C语言 C++
C++入门4——类与对象3-1(构造函数的类型转换和友元详解)
C++入门4——类与对象3-1(构造函数的类型转换和友元详解)
18 1
|
1月前
|
C++
C++番外篇——日期类的实现
C++番外篇——日期类的实现
80 1