C++ 教程 在线

1929C++ 面向对象 C++ 类 & 对象

C++ 中的 struct 对 C 中的 struct 进行了扩充,它已经不再只是一个包含不同数据类型的数据结构了,它已经获取了太多的功能。

struct 能包含成员函数吗? 能!

struct 能继承吗? 能!!

struct 能实现多态吗? 能!!!

既然这些它都能实现,那它和 class 还能有什么区别?

最本质的一个区别就是默认的访问控制,体现在两个方面:

1)默认的继承访问权限。struct是public的,class是private的。

你可以写如下的代码:

struct A
{
    char a;
};
struct B : A
{
    char b;
};

这个时候 B 是 public 继承 A 的。

如果都将上面的 struct 改成 class,那么 B 是 private 继承 A 的。这就是默认的继承访问权限。

所以我们在平时写类继承的时候,通常会这样写:

struct B : public A

就是为了指明是 public 继承,而不是用默认的 private 继承。

当然,到底默认是 public 继承还是 private 继承,取决于子类而不是基类。

我的意思是,struct 可以继承 class,同样 class 也可以继承 struct,那么默认的继承访问权限是看子类到底是用的 struct 还是 class。如下:

struct A{}; 
class B : A{}; //private继承
struct C : B{}; //public继承

2)struct 作为数据结构的实现体,它默认的数据访问控制是 public 的,而 class 作为对象的实现体,它默认的成员变量访问控制是 private 的。

注意我上面的用词,我依旧强调 struct 是一种数据结构的实现体,虽然它是可以像 class 一样的用。我依旧将 struct 里的变量叫数据,class 内的变量叫成员,虽然它们并无区别。

其实,到底是用 struct 还是 class,完全看个人的喜好,你可以将你程序里所有的 class 全部替换成 struct,它依旧可以很正常的运行。但我给出的最好建议,还是:当你觉得你要做的更像是一种数据结构的话,那么用 struct,如果你要做的更像是一种对象的话,那么用 class。

当然,我在这里还要强调一点的就是,对于访问控制,应该在程序里明确的指出,而不是依靠默认,这是一个良好的习惯,也让你的代码更具可读性。

说到这里,很多了解的人或许都认为这个话题可以结束了,因为他们知道 struct 和 class 的“唯一”区别就是访问控制。很多文献上也确实只提到这一个区别。

但我上面却没有用“唯一”,而是说的“最本质”,那是因为,它们确实还有另一个区别,虽然那个区别我们平时可能很少涉及。那就是:“class” 这个关键字还用于定义模板参数,就像 “typename”。但关键字 “struct” 不用于定义模板参数。这一点在 Stanley B.Lippman 写的 Inside the C++ Object Model 有过说明。

问题讨论到这里,基本上应该可以结束了。但有人曾说过,他还发现过其他的“区别”,那么,让我们来看看,这到底是不是又一个区别。还是上面所说的,C++ 中的 struct 是对 C 中的 struct 的扩充,既然是扩充,那么它就要兼容过去 C 中 struct 应有的所有特性。例如你可以这样写:

struct A //定义一个struct
{
    char c1;
    int n2;
    double db3;
};
A a={'p',7,3.1415926}; //定义时直接赋值

也就是说 struct 可以在定义的时候用 {} 赋初值。那么问题来了,class 行不行呢?将上面的 struct 改成 class,试试看。报错!噢~于是那人跳出来说,他又找到了一个区别。我们仔细看看,这真的又是一个区别吗?

你试着向上面的 struct 中加入一个构造函数(或虚函数),你会发现什么?

对,struct 也不能用 {} 赋初值了。

的确,以 {} 的方式来赋初值,只是用一个初始化列表来对数据进行按顺序的初始化,如上面如果写成 A a={'p',7}; 则 c1,n2 被初始化,而 db3 没有。这样简单的 copy 操作,只能发生在简单的数据结构上,而不应该放在对象上。加入一个构造函数或是一个虚函数会使 struct 更体现出一种对象的特性,而使此{}操作不再有效。

事实上,是因为加入这样的函数,使得类的内部结构发生了变化。而加入一个普通的成员函数呢?你会发现{}依旧可用。其实你可以将普通的函数理解成对数据结构的一种算法,这并不打破它数据结构的特性。

那么,看到这里,我们发现即使是 struct 想用 {} 来赋初值,它也必须满足很多的约束条件,这些条件实际上就是让 struct 更体现出一种数据机构而不是类的特性。

那为什么我们在上面仅仅将 struct 改成 class,{} 就不能用了呢?

其实问题恰巧是我们之前所讲的——访问控制!你看看,我们忘记了什么?对,将 struct 改成 class 的时候,访问控制由 public 变为 private 了,那当然就不能用 {} 来赋初值了。加上一个 public,你会发现,class 也是能用 {} 的,和 struct 毫无区别!!!

做个总结,从上面的区别,我们可以看出,struct 更适合看成是一个数据结构的实现体,class 更适合看成是一个对象的实现体。

1928C++ 面向对象 C++ 类 & 对象

关于C++中this指针的理解:当你进入一个房子后,你可以看到房子内的桌子、椅子、地板等;但是你看不到房子的全貌;对于类来说,你可以看到成员函数、成员变量,但你看不到实例本身,但是应用this可以让我们看到这个实例本身。

我的理解:class类就好比这座房子,this就好比一把钥匙,通过钥匙来打开了这座房子的门,那么里面的东西就随意你取用了。

因为this作用域是在类的内部,自己声明一个类的时候,还不知道实例化对象的名字,所以用this来使用对象变量的自身。在非静态成员函数中,编译器在编译的时候加上this作为隐含形参,通过this来访问各个成员(即使你没有写上this指针)

代码实例:

class Point
{ 
    int x, y;
    public:
        Point(int a, int b) { x=a; y=b;}
        void MovePoint( int a, int b){ x+=a; y+=b;}
        void print(){ cout<<"x="< }

    void main( )
    {
         Point point1( 10,10);
         point1.MovePoint(2,2);
         point1.print( );
    }
}

1927C++ 数据结构

可以将结构作为函数的返回值。实例如下:

#include <stdio.h>
#include <stdlib.h>

struct test{
    int i;
    char c;
    double d;
    float f;
};

struct test set( int a, float b, char c, double d )
{
    struct test t;
    t.i = a;
    t.f = b;
    t.c = c;
    t.d = d;
    return t;
}

void sig( int s );
void print(struct test t2)
{
    printf("int:%d\n",t2.i);
    printf("char:%c\n",t2.c);
    printf("float:%f\n",t2.f);
    printf("double:%lf\n",t2.d);
}

void sig( int s )
{
    printf("捕获信号%d,跳出……",s);
    exit(1);
}
int main( void )
{
    struct test info;
    info = set(2,3.22,'d',4.335);
    print(info);
    return 0;
}

1926C++ 数据结构

.-> 运算符

.(点)运算符和 ->(箭头)运算符用于引用类、结构和共用体的成员: 点运算符应用于实际的对象。箭头运算符与一个指向对象的指针一起使用。

例如,假设有下面的结构:

struct Employee {
  char first_name[16];
  int  age;
} emp;

. 点运算符

下面的代码把值 zara 赋给对象 emp 的 first_name 成员:

strcpy(emp.first_name, "zara");

-> 箭头运算符

如果 p_emp 是一个指针,指向类型为 Employee 的对象,则要把值 zara 赋给对象 emp 的 first_name 成员,需要编写如下代码:

strcpy(p_emp->first_name, "zara");

-> 称为箭头运算符,它是由一个减号加上一个大于号组成。

简而言之,访问结构的成员时使用点运算符,而通过指针访问结构的成员时,则使用箭头运算符。

也就是说,用结构体定义了一个实体,那么这个实体要引用他里面的成员,就用 . 操作符,如果用结构体定义的是一个结构指针,那么要引用他里面的成员就用 ->

1925C++ 数据结构

typedef 的省略不是因为 c++ 可以省略,Mac OS 的 Xcode 就不可以省略的。

补充一下,C ++ 中的 typedef struct 是会对最后的 object_names 部分产生两种影响:

  • 第一是不用 typedef:object_names 就相当于一个变量了,可以直接调用结构体中的内容
  • 第二种是用 typedef:object_names 部分是个结构体类型,在调用结构体之前必须创建结构体的变量 object_names name;