C++类基础

本文详细介绍了C++中的类,包括类声明与定义、对象的存储空间、静态成员、构造函数、复制构造函数、运算符重载、虚函数、抽象类以及C++11中的`override`和`final`关键字。重点讲解了构造函数的初始化列表、虚函数表的原理、派生类构造函数的执行顺序,以及如何避免二义性问题和使用虚基类解决多继承的冲突。
摘要由CSDN通过智能技术生成

1. 类

1.1 类声明与类定义

(1)类声明

class a;//声明一个类,而不是定义

  在声明之后,定义之前,类a是一个不完全类型,即已知类a是一个类型,但不知道其包含了哪些成员。
  不完整类型使用方式是有限的。不能定义不完整类型的对象,不完全类型只能定义指向该类型的指针或者引用,或者用于声明(而不是定义)使用该类型作为形参类型或返回类型的函数
  在创建类的对象之前,必须要完整的定义该类,而不只是声明。(定义是要分配存储空间的,定义即声明,但声明不是定义)同样,在使用引用和指针访问对象成员之前,必须已经定义了类。

(2)为类的成员使用类声明

  只有当类定义已经在前面出现过,数据成员才能被指定为该类类型。如果该类型是不完全类型,那么数据成员只能是指向该类类型的指针或引用。
  因为只有当类定义体完成后才能定义类,因此类不能具有自身类型的数据成员,然而,只要类名一出现,就可以认为该类已经声明。因此,类的数据成员可以是指向自身类型的指针或引用

class val{
    int time;
    int price;
};
class node{
    val v;//因为val类前面已经定义了,所以可以用val类来定义对象
    node *next;//因为在这个地方node类还没有完全定义,所以属于不完全类型,因此不能出现用node类定义对象,但只要类名出现,就可以认为声明了,所以可以用node类来定义指向该类型的指针或引用
};

1.2 对象的存储空间

  • 一般类定义的时候不进行存储空间的分配(有静态数据成员的,会在定义类的时候为其分配空间),而在用类定义对象的时候,将为这个类的对象进行存储空间的分配。
  • 对象的存储空间主要包含的是类的数据成员部分所占的空间
    • 类的大小(对象的大小),一般指非静态数据成员的大小,要考虑到数据对齐,不包含成员方法,因为那是代码,存放在代码区,且所有对象共有,公用的成员方法通过隐含的this指针判断对象调用的,然后该方法就可以去访问那个对象的数据空间了。
    • 如果类中包含虚函数,那么类对象在内存中还会被自动包含一个指向虚函数表的指针,也是占空间的, 一般这个指针在对象所在内存中起始地址处。
    • 如果类是没有定义任何数据成员,类的大小或对象的大小本应该是0,但因为一个对象往往是独一无二的,体现在内存中,是不同对象的地址是不同的,所以编译器会让这个空对像占一个字节,以便不同对象的起始地址不同。

1.3 static成员

(1)static成员函数

  • static成员函数没有this指针,因为static成员,包括数据成员和成员函数,都是类的组成部分,而不是任何对象的组成部分,因此static方法是没有this指针的。
  • 也是因为static成员不属于任何对象,因此static成员函数不能访问任何对象的数据成员,他只能访问类的static数据成员或其他的static成员方法。
  • static成员函数不能声明为虚函数。

(2)static数据成员

  • 普通static数据成员

    • 即非const的static数据成员,它必须在类定义体的外部定义,只定义一次。不像普通的数据成员,static数据成员不是通过构造函数进行初始化的。

      class A{
          private://public也行
              static int i;
          ....
      };
      static int A::i = 1;//error
      int A::i = 1;//ok
      int A::i;//ok,不显示初始化也正确,到时候会默认初始化为0
      

      【注意1】:这里static关键字只能用于类定义体内部,在外面定义时不能标识为static。
      【注意2】:且不管static数据成员是私有的还是共有的,都可以在类外定义初始化。
      【注意3】:sizeof类时候,计算的类大小是不包含static成员的,因为静态成员不占类和对象的空间。同理可以说明:静态成员的地址并不在对象的地址范围内
      【注意4!!!!!】:static变量必须在类外初始化,否则会报错。因为类的定义不占空间,类的定义只是给编译器说明这个类型有多大,好在定义对象的时候分配空间,因此不像变量的定义的那样,定义即分配空间,这里类的定义中,所有数据成员都只是声明,static类型也不例外,那为什么说static数据成员在类定义时就分配了空间了呢?因为static数据成员必须在类外定义,这个定义可以显示初始化,但一定要有这个类外的定义!!!!

  • 特殊的const static数据成员

    • 一般而言,类的static数据成员,和普通数据成员一样,不能在类的定义体中初始化。
    • 这个规则有个例外,如果是const static数据成员,则可以在类体中定义该数据成员时初始化。

      class A{
      private://public也行
          const static int i = 1;//ok
      };
      
  • static成员不是类对象的组成部分

    • static数据成员的类型可以是该成员所属的类类型,而非static数据成员被限定声明为指向其自身类类型的指针或引用。

      class A{
          private:
              static A mem1;//ok
              A mem2;//error
              A &mem3;//ok
              A *mem4;//ok
      };
      A::mem1(..)//static 类对象的构造函数
      

1.4 构造函数

(1)构造函数初始化列表

  构造函数的工作是保证每个对象的数据成员(除去static,因为static成员不属于对象)具有合适的初始值,一般建议使用构造函数初始化列表。
  构造函数初始化列表是以一个冒号开始,接着是一个以逗号分隔的数据成员列表,每个数据成员后面跟着一个放在圆括号中的初始化式

class A{
    private:
        int a;
        char c;
        string s;
    public:
        A():a(1),c('a'),s("hello");//erro
        A();
        或
        A():a(1),c('a'),s("hello")//ok
        {
        }
};
A::A():a(1),c('a'),s("hello");//ok

构造函数初始化列表不能在构造函数声明的时候出现,只在构造函数的定义时出现。
构造函数分两个阶段执行:

  1. 先执行初始化阶段(即执行初始化列表,属于显式初始化)
  2. 然后执行构造函数函数体阶段,该阶段不是初始化数据了,而是重新赋值。

对于在构造函数初始化列表中没有显式提及的每个成员

  • 内置类型(int、char、double、枚举等)或者复合类型(数组指针等)的成员的初始化值依赖于对象的作用域:
    • 对象在局部作用域中时,这些成员不被初始化
    • 对象在全局作用域中它们被初始化为0
  • 对于类类型数据成员,不管在哪个作用域,都运行该类型的默认构造函数来初始化,所以对于没有定义默认构造函数(无形参的构造函数)的类类型数据成员,一定要显式地在构造函数初始化列表中调用该类的带参数的构造函数。

总之:对于用类定义一个对象,会执行对象的构造函数,而执行构造函数,首先会执行构造函数初始化列表,如果有数据成员不在初始化列表中,则按上述原则进行初始化。然后再去执行构造函数函数体,但已经不是初始化了,而是重新赋值。

class A{
    public:
        int i;
    A():i(1024){}
};
class B{
    public:
        int j;
        A a;//因为在类的定义中,所以这是声明,不是定义对象a,定义是要分配空间的。
}
void fun(){
    B b;
    cout << b.j <<" "<<b.a.i<<endl;
}
B gb;
int main(){
    cout << gb.j<<" "<<gb.a.i<<endl;
    fun();
    return 0;
}

输出的值是

0 1024
不确定值 1024

如果是以下情况:

class A
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值