关闭

C++ 基础知识

198人阅读 评论(0) 收藏 举报
分类:

1.什么是拷贝构造函数:

拷贝构造函数嘛,当然就是拷贝和构造了。(其实很多名字,只要静下心来想一想,就真的是顾名思义呀)拷贝又称复制,因此拷贝构造函数又称复制构造函数。百度百科上是这样说的:拷贝构造函数,是一种特殊的构造函数,它由编译器调用来完成一些基于同一类的其他对象的构建及初始化。其唯一的参数(对象的引用)是不可变的(const类型)。此函数经常用在函数调用时用户定义类型的值传递及返回

 

2.拷贝构造函数的形式

Class X

{

public:

X();

X(const X&);//拷贝构造函数

}

2.1为什么拷贝构造参数是引用类型?

其原因如下:当一个对象以传递值的方式传一个函数的时候,拷贝构造函数自动被调用来生成函数中的对象(符合拷贝构造函数调用的情况)。如果一个对象是被传入自己的拷贝构造函数,它的拷贝构造函数将会被调用来拷贝这个对象,这样复制才可以传入它自己的拷贝构造函数,这会导致无限循环直至栈溢出(Stack Overflow)。

 

3.拷贝构造函数调用的三种形式

3.1.一个对象作为函数参数,以值传递的方式传入函数体;

3.2.一个对象作为函数返回值,以值传递的方式从函数返回;

3.3.一个对象用于给另外一个对象进行初始化(常称为复制初始化)。

总结:当某对象是按值传递时(无论是作为函数参数,还是作为函数返回值),编译器都会先建立一个此对象的临时拷贝,而在建立该临时拷贝时就会调用类的拷贝构造函数

 

4.深拷贝和浅拷贝

如果在类中没有显式地声明一个拷贝构造函数,那么,编译器将会自动生成一个默认的拷贝构造函数,该构造函数完成对象之间的位拷贝。(位拷贝又称浅拷贝,后面将进行说明。)自定义拷贝构造函数是一种良好的编程风格,它可以阻止编译器形成默认的拷贝构造函数,提高源码效率。

在某些状况下,类内成员变量需要动态开辟堆内存,如果实行位拷贝,也就是把对象里的值完全复制给另一个对象,如A=B。这时,如果B中有一个成员变量指针已经申请了内存,那A中的那个成员变量也指向同一块内存。这就出现了问题:当B把内存释放了(如:析构),这时A内的指针就是野指针了,出现运行错误。事实上这就要用到深拷贝了,要自定义拷贝构造函数。

深拷贝和浅拷贝可以简单理解为:如果一个类拥有资源,当这个类的对象发生复制过程的时候,资源重新分配,这个过程就是深拷贝,反之,没有重新分配资源,就是浅拷贝。下面举个深拷贝的例子。

#include <iostream>

using namespace std;

class CA

{

public:

CA(int b,char*cstr)

{

a=b;

str=new char[b];

strcpy(str,cstr);

}

CA(const CA& C)

{

a=C.a;

str=new char[a];//深拷贝

if(str!=0)

strcpy(str,C.str);

}

void Show()

{

cout<<str<<endl;

}

~CA()

{

delete str;

}

private:

int a;

char *str;

};

int main()

{

CAA(10,"Hello!");

CA B=A;

B.Show();

return 0;

}

浅拷贝资源后在释放资源的时候会产生资源归属不清的情况导致程序运行出错。一定要注意类中是否存在指针成员。

 

 

5.拷贝构造函数与“=“赋值运算符

例如:

class CExample

{}

int main()

{

CExample e1 = new CExample;

CExample e2 = e1;//调用拷贝构造函数

CExample e3(e1);//调用拷贝构造函数

CExample e4;

e4 = e1;//调用=赋值运算符

}

通常的原则是:对于凡是包含动态分配成员或包含指针成员的类都应该提供拷贝构造函数;在提供拷贝构造函数的同时,还应该考虑重载"="赋值操作符号。

分类: C++

 

 

一、什么是操作符重载

操作符重载可以分为两部分:操作符重载。说到重载想必都不陌生了吧,这是一种编译时多态,重载实际上可以分为函数重载和操作符重载。运算符重载和函数重载的不同之处在于操作符重载重载的一定是操作符。我们不妨先直观的看一下所谓的操作符重载:

 1 #include <iostream>

 2

 3 using namespace std;

 4

 5 int main()

 6 {

 7     int a = 2 , b = 3;

 8     float c = 2.1f , d = 1.2f;

 9     cout<<"a + b = "<<a+b<<endl;

10    cout<<"c + d ="<<c+d<<endl;

11     return 0;

12 }

我们看到操作符“+”完成floatint两种类型的加法计算,这就是操作符重载了。这些内置类型的操作符重载已经实现过了,但是如果现在我们自己写过的类也要实现实现类似的加法运算,怎么办呢??比如现在现在有这样一个点类point,要实现两个点的相加,结果是横纵坐标都要相加,这时候就需要我们自己写一个操作符重载函数了。

View Code

 

二、实现操作符重载的两种方式

操作符重载的实现方式有两种,即通过友元函数或者类成员函数

1.友元函数重载操作符的格式:

1 class 类名

2 {

3    friend 返回类型 operator 操作符(形参表);

4 };

5 //类外定义格式:

6 返回类型 operator操作符(参数表)

7 {

8     //函数体

9 }

2.类成员函数实现操作符重载的格式:

 1 class 类名

 2 {

 3 public:

 4     返回类型 operator 操作符(形参表);

 5 };

 6 //类外定义格式

 7 返回类型 类名::operator 操作符(形参表)

 8 {

 9     //函数体

10 }

这样说吧,还是不足以比较这两种实现方式的区别,我们分别用两种实现方式写point类的”+“”-“的重载。代码如下:

View Code

这里不知道大家看到没有,利用友元函数重载二元操作符”-“时,形式参数是两个,而利用类成员函数时,形式参数却只有一个。这时因为类成员函数中存在this指针,这相当于一个参数,所以类成员实现操作符重载需要的形式参数比原来少一个,这比如:利用类成员函数实现一元操作符”-“,就不需要参数了。也正是因为这个原因,友元函数实现的操作符重载是有限制的,比如:[] ,(),-> =不能利用友元函数实现运算符的重载。

在实际开发过程中,单目运算符建议重载为成员函数,而双目运算符建议重载为友元函数。通常下双目运算符重载为友元函数比重载为成员函数更方便,但是有时双目运算符必须重载为成员函数,例如赋值运算符=。还有如果需要修改对象内部的状态,一般可以选择利用类成员函数进行修改。

 

三、运算符重载的原则

这样一看,运算符重载还是蛮简单的嘛,实际上运算符重载也是要遵循一些原则的:

1.C++中只能对已有的C++运算符进行重载,不允许用户自己定义新的运算符。

2.C++中绝大部分的运算符可重载,除了成员访问运算符.,作用域运算符::长度运算符sizeof以及条件运算符?:

3.运算符重载后不能改变运算符的操作对象(操作数)的个数。如:"+"是实现两个操作数的运算符,重载后仍然为双目运算符。

4.重载不能改变运算符原有的优先级和原有的结合性。

6.运算符重载不能全部是C++中预定义的基本数据,这样做的目的是为了防止用户修改用于基本类型数据的运算符性质。

 

四、为什么要进行运算符重载

关于运算符重载要遵循这么多原则,那么为什么还要进行运算符重载呢?为什么我不是写一个add()函数,代替operator +()呢??个人感觉C++中之所以要支持运算符的重载是为了与内置数据类型统一操作,比如:c = a + b c = adda,b),这看起来哪个更直观一点呢,显然是前者了。同时,我们希望操作我们自己定义的数据类型能像操作intdouble这些内置数据类型一样方便。可能举这个加法的例子有点不好,现在加入重载的运算符是[],<<,^,|等呢?这时我们要用什么成员函数代替呢??代替之后又是一种什么效果呢?会一眼就看出来这个函数要干什么吗??

 

思考:C++中的++运算符是有两种的,这个我们怎么区别重载的是前自增还是后自增呢?

 

 

C++模板

  模板是C++支持参数化多态的工具,使用模板可以使用户为类或者函数声明一种一般模式,使得类中的某些数据成员或者成员函数的参数、返回值取得任意类型。

  模板是一种对类型进行参数化的工具;

  通常有两种形式:函数模板类模板

  函数模板针对仅参数类型不同的函数

  类模板针对仅数据成员成员函数类型不同的类。

使用模板的目的就是能够让程序员编写与类型无关的代码。比如编写了一个交换两个整型int 类型的swap函数,这个函数就只能实现int 型,对double,字符这些类型无法实现,要实现这些类型的交换就要重新编写另一个swap函数。使用模板的目的就是要让这程序的实现与类型无关,比如一个swap模板函数,即可以实现int 型,又可以实现double型的交换。模板可以应用于函数和类。下面分别介绍。

注意:模板的声明或定义只能在全局,命名空间或类范围内进行。即不能在局部范围,函数内进行,比如不能在main函数中声明或定义一个模板。

 

 一、函数模板通式


1、函数模板的格式:

template <class 形参名class 形参名,......返回类型函数名(参数列表)

{

      函数体

}

  其中templateclass是关见字,class可以用typename 关见字代替,在这里typename class没区别<>括号中的参数叫模板形参,模板形参和函数形参很相像,模板形参不能为空。一但声明了模板函数就可以用模板函数的形参名声明类中的成员变量和成员函数,即可以在该函数中使用内置类型的地方都可以使用模板形参名。模板形参需要调用该模板函数时提供的模板实参来初始化模板形参,一旦编译器确定了实际的模板实参类型就称他实例化了函数模板的一个实例。比如swap的模板函数形式为

template<class T> void swap(T& a, T& b){}

当调用这样的模板函数时类型T就会被被调用时的类型所代替,比如swap(a,b)其中abint 型,这时模板函数swap中的形参T就会被int 所代替,模板函数就变为swap(int &a, int &b)。而当swap(c,d)其中cddouble类型时,模板函数会被替换为swap(double &a,double &b),这样就实现了函数的实现与类型无关的代码。

2、注意:对于函数模板而言不存在 h(int,int) 这样的调用,不能在函数调用的参数中指定模板形参的类型,对函数模板的调用应使用实参推演来进行,即只能进行 h(2,3) 这样的调用,或者int a, b; h(a,b)

函数模板的示例演示将在下文中涉及!

二、类模板通式


1、类模板的格式为:

template<class  形参名class 形参名>   class 类名

{ ... };

  类模板和函数模板都是以template开始后接模板形参列表组成,模板形参不能为空,一但声明了类模板就可以用类模板的形参名声明类中的成员变量和成员函数,即可以在类中使用内置类型的地方都可以使用模板形参名来声明。比如

template<classT> class A{public: T a; T b; T hy(T c, T &d);};

在类A中声明了两个类型为T的成员变量ab,还声明了一个返回类型为T带两个参数类型为T的函数hy

2、类模板对象的创建:比如一个模板类A,则使用类模板创建对象的方法为A<int> m;在类A后面跟上一个<>尖括号并在里面填上相应的类型,这样的话类A中凡是用到模板形参的地方都会被int 所代替。当类模板有两个模板形参时创建对象的方法为A<int,double> m;类型之间用逗号隔开。

3、对于类模板,模板形参的类型必须在类名后的尖括号中明确指定。比如A<2> m;用这种方法把模板形参设置为int是错误的(编译错误:error C2079: 'a'uses undefined class 'A<int>'),类模板形参不存在实参推演的问题。也就是说不能把整型值2推演为int 型传递给模板形参。要把类模板形参调置为int 型必须这样指定A<int> m

4、在类模板外部定义成员函数的方法为:

template<模板形参列表> 函数返回类型类名<模板形参名>::函数名(参数列表){函数体}

比如有两个模板形参T1T2的类A中含有一个void h()函数,则定义该函数的语法为:

template<classT1,class T2> void A<T1,T2>::h(){}

注意:当在类外面定义类的成员时template后面的模板形参应与要定义的类的模板形参一致。

5、再次提醒注意:模板的声明或定义只能在全局,命名空间或类范围内进行。即不能在局部范围,函数内进行,比如不能在main函数中声明或定义一个模板。

 

三、模板的形参


有三种类型的模板形参:类型形参,非类型形参和模板形参。

  1、类型形参

1.1 、类型模板形参:类型形参由关见字classtypename后接说明符构成,如template<class T> void h(T a){};其中T就是一个类型形参,类型形参的名字由用户自已确定。模板形参表示的是一个未知的类型。模板类型形参可作为类型说明符用在模板中的任何地方,与内置类型说明符或类类型说明符的使用方式完全相同,即可以用于指定返回类型,变量声明等。

作者原版:1.2 不能为同一个模板类型形参指定两种不同的类型,比如template<class T>void h(T a, T b){}语句调用h(2, 3.2)将出错,因为该语句给同一模板形参T指定了两种类型,第一个实参2把模板形参T指定为int,而第二个实参3.2把模板形参指定为double,两种类型的形参不一致,会出错。(针对函数模板)

    作者原版:1.2针对函数模板是正确的,但是忽略了类模板。下面将对类模板的情况进行补充。

本人添加1.2补充版(针对于类模板)、当我们声明类对象为:A<int> a,比如template<classT>T g(T a, T b){},语句调用a.g(2, 3.2)在编译时不会出错,但会有警告,因为在声明类对象的时候已经将T转换为int类型,而第二个实参3.2把模板形参指定为double,在运行时,会对3.2进行强制类型转换为3。当我们声明类的对象为:A<double> a,此时就不会有上述的警告,因为从intdouble是自动类型转换。

演示示例1:

TemplateDemo.h

 1 #ifndef TEMPLATE_DEMO_HXX

 2 #define TEMPLATE_DEMO_HXX

 3

 4 template<class T> class A{

 5     public:

 6         T g(T a,T b);

 7         A();

 8 };

 9

10 #endif

TemplateDemo.cpp

 1 #include<iostream.h>

 2 #include "TemplateDemo.h"

 3

 4 template<class T> A<T>::A(){}

 5

 6 template<class T> T A<T>::g(T a,T b){

 7     return a+b;

 8 }

 9

10 void main(){

11     A<int> a;

12     cout<<a.g(2,3.2)<<endl;

13 }

  编译结果:

1 --------------------Configuration:TemplateDemo - Win32 Debug--------------------

2 Compiling...

3 TemplateDemo.cpp

4 G:\C++\CDaima\TemplateDemo\TemplateDemo.cpp(12) : warning C4244: 'argument' :conversion from 'const double' to 'int', possible loss of data

5

6 TemplateDemo.obj - 0 error(s), 1warning(s)

运行结果:      5

  我们从上面的测试示例中可以看出,并非作者原作中的那么严密!此处仅是本人跟人测试结果!请大家本着实事求是的态度,自行验证!

 

  2、非类型形参

2.1 、非类型模板形参:模板的非类型形参也就是内置类型形参,如template<class T, int a> class B{};其中int a就是非类型的模板形参。

2.2非类型形参在模板定义的内部是常量值,也就是说非类型形参在模板的内部是常量。

2.3 非类型模板的形参只能是整型,指针和引用,像doubleString, String**这样的类型是不允许的。但是double &double *对象的引用或指针是正确的。

2.4 调用非类型模板形参的实参必须是一个常量表达式,即他必须能在编译时计算出结果。

2.5 、注意:任何局部对象,局部变量,局部对象的地址,局部变量的地址都不是一个常量表达式,都不能用作非类型模板形参的实参。全局指针类型,全局变量,全局对象也不是一个常量表达式,不能用作非类型模板形参的实参。

2.6 全局变量的地址或引用,全局对象的地址或引用const类型变量是常量表达式,可以用作非类型模板形参的实参

2.7 sizeof表达式的结果是一个常量表达式,也能用作非类型模板形参的实参。

2.8 、当模板的形参是整型时调用该模板时的实参必须是整型的,且在编译期间是常量,比如template<class T, int a> class A{};如果有int b,这时A<int, b>m;将出错,因为b不是常量,如果const int b,这时A<int, b>m;就是正确的,因为这时b是常量。

2.9 非类型形参一般不应用于函数模板中,比如有函数模板template<class T, int a> void h(T b){},若使用h(2)调用会出现无法为非类型形参a推演出参数的错误,对这种模板函数可以用显示模板实参来解决,如用h<int, 3>(2)这样就把非类型形参a设置为整数3。显示模板实参在后面介绍。

2.10非类型模板形参的形参和实参间所允许的转换
1、允许从数组到指针,从函数到指针的转换。如:template <int*a> class A{}; int b[1]; A<b> m;即数组到指针的转换
2const修饰符的转换。如:template<constint *a> class A{}; int b; A<&b> m;   即从int *const int *的转换。
3、提升转换。如:template<inta> class A{}; const short b=2; A<b> m; 即从shortint 的提升转换
4、整值转换。如:template<unsignedint a> class A{};   A<3> m; 即从int unsigned int的转换。
5、常规转换。

非类型形参演示示例1

由用户自己亲自指定栈的大小,并实现栈的相关操作。

TemplateDemo.h

 1 #ifndef TEMPLATE_DEMO_HXX

 2 #define TEMPLATE_DEMO_HXX

 3

 4 template<class T,int MAXSIZE> classStack{//MAXSIZE由用户创建对象时自行设置

 5     private:

 6         T elems[MAXSIZE];    // 包含元素的数组

 7         intnumElems;    // 元素的当前总个数

 8     public:

 9         Stack();    //构造函数

10        void push(T const&);    //压入元素

11        void pop();       //弹出元素

12        T top() const;    //返回栈顶元素

13        bool empty() const{     // 返回栈是否为空

14            return numElems == 0;

15        }

16        bool full() const{    // 返回栈是否已满

17            return numElems == MAXSIZE;

18        }

19 };

20

21 template <class T,int MAXSIZE>

22 Stack<T,MAXSIZE>::Stack():numElems(0){     // 初始时栈不含元素

23     // 不做任何事情

24 }

25

26 template <class T,int MAXSIZE>

27 void Stack<T, MAXSIZE>::push(T const& elem){

28     if(numElems == MAXSIZE){

29        throw std::out_of_range("Stack<>::push(): stack is full");

30     }

31    elems[numElems] = elem;   // 附加元素

32    ++numElems;               // 增加元素的个数

33 }

34

35 template<class T,int MAXSIZE>

36 void Stack<T,MAXSIZE>::pop(){

37     if (numElems <= 0) {

38        throw std::out_of_range("Stack<>::pop(): empty stack");

39     }

40    --numElems;               // 减少元素的个数

41 }

42

43 template <class T,int MAXSIZE>

44 T Stack<T,MAXSIZE>::top()const{

45     if (numElems <= 0) {

46        throw std::out_of_range("Stack<>::top(): empty stack");

47     }

48     return elems[numElems-1];  // 返回最后一个元素

49 }

50

51 #endif

TemplateDemo.cpp

 1 #include<iostream.h>

 2 #include <iostream>

 3 #include <string>

 4 #include <cstdlib>

 5 #include "TemplateDemo.h"

 6

 7 int main(){

 8     try {

 9         Stack<int,20> int20Stack;  // 可以存储20个int元素的栈

10        Stack<int,40>  int40Stack; // 可以存储40个int元素的栈

11        Stack<std::string,40> stringStack; // 可存储40个string元素的栈

12

13        // 使用可存储20个int元素的栈

14        int20Stack.push(7);

15        std::cout << int20Stack.top() << std::endl;    //7

16        int20Stack.pop();

17

18        // 使用可存储40个string的栈

19        stringStack.push("hello");

20        std::cout << stringStack.top() << std::endl;    //hello

21        stringStack.pop();   

22        stringStack.pop();    //Exception: Stack<>::pop<>:empty stack

23        return 0;

24     }

25     catch (std::exception const& ex) {

26        std::cerr << "Exception:" << ex.what()<< std::endl;

27        return EXIT_FAILURE;  // 退出程序且有ERROR标记

28     }

29 }

  运行结果:

 非类型形参演示示例2

TemplateDemo01.h

 1 #ifndef TEMPLATE_DEMO_O1

 2 #define TEMPLATE_DEMO_01

 3

 4 template<typename T> class CompareDemo{

 5     public:

 6         intcompare(const T&, constT&);

 7 };

 8

 9 template<typename T>

10 int CompareDemo<T>::compare(const T& a,const T& b){

11     if((a-b)>0)

12        return 1;

13     else if((a-b)<0)

14        return -1;

15     else

16        return 0;

17 }

18

19 #endif

TemplateDemo01.cpp

1 #include<iostream.h>

2 #include "TemplateDemo01.h"

3

4 void main(){

5     CompareDemo<int> cd;

6     cout<<cd.compare(2,3)<<endl;

7 }

  运行结果:     -1

1 #include<iostream.h>

2 #include "TemplateDemo01.h"

3

4 void main(){

5     CompareDemo<double> cd;

6     cout<<cd.compare(3.2,3.1)<<endl;

7 }

 运行结果:      1

TemplateDemo01.h 改动如下:

 1 #ifndef TEMPLATE_DEMO_O1

 2 #define TEMPLATE_DEMO_01

 3

 4 template<typename T> class CompareDemo{

 5     public:

 6         int compare(T&, T&);

 7 };

 8

 9 template<typename T>

10 int CompareDemo<T>::compare(T& a,T& b){

11     if((a-b)>0)

12        return 1;

13     else if((a-b)<0)

14        return -1;

15     else

16        return 0;

17 }

18

19 #endif

TempalteDemo01.cpp

1 #include<iostream.h>

2 #include "TemplateDemo01.h"

3

4 void main(){

5    CompareDemo<int> cd;

6     int a=2,b=3;

7     cout<<cd.compare(a,b)<<endl;

8 }

 

 非类型形参演示示例3

TemplateDemo02.cpp

 1 #include<iostream.h>

 2

 3 template<typename T>

 4 const T& max(constT& a,const T& b){

 5     returna>b ? a:b;

 6 }

 7

 8 void main(){

 9     cout<<max(2.1,2.2)<<endl;//模板实参被隐式推演成double

10    cout<<max<double>(2.1,2.2)<<endl;//显示指定模板参数。

11    cout<<max<int>(2.1,2.2)<<endl;//显示指定的模板参数,会将函数函数直接转换为int

12 }

  运行结果:

cout<<max<int>(2.1,2.2)<<endl;//显示指定的模板参数,会将函数函数直接转换为int。此语句会出现警告:

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

对于const类对象/指针/引用,只能调用类的const成员函数,因此,const修饰成员函数的最重要作用就是限制对于const对象的使用。

a. const成员函数不被允许修改它所在对象的任何一个数据成员。

b. const成员函数能够访问对象的const成员,而其他成员函数不可以。

 

(3)const修饰类对象/对象指针/对象引用

·             const修饰类对象表示该对象为常量对象,其中的任何成员都不能被修改。对于对象指针和对象引用也是一样。

·             const修饰的对象,该对象的任何非const成员函数都不能被调用,因为任何非const成员函数会有修改成员变量的企图。
例如:
class AAA
{
 
    void func1();
 
void func2() const;
 
}
 
const AAA aObj;
 
aObj.func1();
 ×
aObj.func2();
 正确

const AAA* aObj = new AAA();
 
aObj-> func1();
 ×
aObj-> func2();
 正确

 

 

class A

private

 B *b;

这样,当a需要的时候可以调用B类的函数方法

这样的缺点是:

1A可以使用B的方法,B不可以使用A的方法(B看不到A);

2:内聚性弱,也就是a对象消失b对象一定消失(想像一下ab是两个窗口,a是登陆,b是主页,当点击a上的按钮时生成b,但a界面不能消失释放,但实际情况下登陆成功后登陆界面消失),b对象生成失败a对象也就失败。

3:如果三个类,四个类,五个类之间交流,互相交流,但上述方法不可能实现

这个问题一直困扰了我很久,上述简单处理虽然可以解决一些基本的问题,但事实上两个类是如何通信的呢?

 

其实方法有很多,大体思路是这样的:

 

定义一个类M,在M中让AB作为其成员变量,(这样M中的函数就可以调用AB中的函数方法),当A中想要发消息给B时,想方设法把消息给MM再调用B中的函数传给B,那么这个"想方设法"是什么方法呢?

开始我是这么想的

M::M()

a=new A(this);//把当前的this指针传给a,这样在a的函数里就可以调用M的函数了。

 编译会发生错误

什么捏?  是这样滴:在写类M的时候,要包含头文件#include“A.h”有木有!!,在写类A的函数的时候要包含头文件#include"M.h"有木有!! (因为在A的构造里要定义一个M类型的指针做实参啊!),这里就有问题,计算机执行时,1:走到#include“A.h”这一行代码时要先看看A.h有什么。2:走到A.h里的时候,发现有#include"M.h"这行代码,然后又进入M.h看看,3:执行1步骤。。。。。是不是循环,无限循环有木有!!!所以报错。

 

解决方法有很多,例如监听A的函数,利用函数指针,利用全局函数(全局函数简单好用,但破坏了面向对象的封装性)

好了,不罗嗦了,正确答案如下:

用接口函数,interface,在c++下没有定义接口函数,其实接口函数就是纯虚函数,在vc下(别的我不知道行不行)可以这样:

‍‍‍#include  

using namespace std;
#define interface class __declspec(novtable)
interface InterFace

‍ virtual void f();

};

 为了解决那个让计算机迷糊的问题,我们让M继承InterFace,然后传参的时候传InterFace类型的,这样就不会重复包含了,试试:

#include"Interface.h"

#include"A.h"

#include"B.h"

class M public InterFace

private

A *a;

B *b;

public:

M()//构造

a=new A(this);//因为M继承InterFace所以可以用父类的指针来作为它的实参!

b=new B();

void f()

{

b->show();//调用B类方法

}

;

 

A类:

#include"InterFace.h"

class A

{

public:

‍A(InterFace *i)

{

printf("A要调用M中的f()函数,让f()函数调用B的函数,这样A就成功通信到了B");

‍i->f();//这里调用的是InterFacef函数,不过在M类中实例化了

}

};

B类:

#include"stdio.h" 

class B

{

public:

void show()

{

‍printf"我被M调用了!";

}

};

 int mian()

{

M m;

return 0;

 

0
0

查看评论
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
    个人资料
    • 访问:50117次
    • 积分:595
    • 等级:
    • 排名:千里之外
    • 原创:13篇
    • 转载:234篇
    • 译文:2篇
    • 评论:1条
    最新评论