类和对象

  1. 封装和抽象的不同之处:前者binding,后者hiding,通过封装可以实现抽象

  2. 类没有size一说

    类对象有size,取决于all data members

  3. 如果一个类的成员都是私有的,它的隐式构造函数也还是要使用public说明符

    在不能创建实例的类中,构造函数需要用private说明

    简单来说就是,类外创建实例,只能通过public的构造函数创建

    构造函数是private的,只能在父类中创建实例,不能继承
    
    构造函数是protected的,还可以在子类中创建实例
    
    构造函数是public的,可以在程序中任何地方创建实例
    
  4. 私有的成员函数不能被重写(override),但是可以被重载(overload)

  5. 对于这段代码,每一次当对象被创建的时候,z将会被使用,但x和y不一定会

    1
    2
    3
    4
    5
    class A{
    int x, y, z;
    public:
    A(){ y = 100; x = 100 * y; }
    }
  6. 类的成员变量中,被声明为private的是最安全的,不是protected

    但是吧,又说,protected的成员变量和private的一样安全,而且可以被继承

    class中,默认成员是private的,如果主动声明为protected,就可以在包外通过继承访问成员,否则private包外访问不了

  7. 静态成员遵循类成员访问规则(public, private, protected)

    静态成员函数不和任何对象关联,当调用静态成员函数时,也没有this指针

    静态成员函数不能是virtual,const,volatile的

  8. 数据成员只能用构造函数初始化,但静态成员变量必须在类外初始化(确保其唯一确定性)

    因为静态成员变量属于整个类,而不是类的特定实例,而普通的实例变量是每个类的实例特有的,因此可以在构造函数或原位初始化

  9. member function:属于类的函数

    将其概括为“在类中定义的函数”是不准确的,因为通过继承得到的函数,并不在类中定义,但一旦继承就会属于该类

    成员函数有五种类型:simple,static,const,inline,friend

    常量成员函数:不改变调用对象值的函数,当返回值是成员变量的引用的时候,不能const修饰;但如果是成员外变量的引用,是可以用const修饰的

  10. Properties of an object: properties, identity, attributes

    names不是

  11. 对象是类的实例,程序运行期间对象的类型一经确定无法改变

    当且仅当构造函数被调用之后,操作系统才会给这个对象分配内存空间,大小取决于data members,但不包括静态成员变量

    两个对象可以指向相同的内存位置

    单例对象:程序运行期间,该类只有这一个实例

  12. 当函数返回一个对象时,会隐式地创建一个临时对象,然后把值赋给临时对象(把变量值逐一拷贝过去),并返回这个临时对象

    局部变量不能通过引用返回,否则会compile time error

    因为引用返回,实际上是让对象指向这个值的内存地址,然而局部变量直接销毁了,指不过去的

  13. 如果函数传参直接传对象,那么拷贝构造函数将会被调用,把该对象复制给函数的局部变量中

    如果函数传参想通过对象传数据成员,要求该数据成员是public的而且它是被传递给类外函数的

    如果函数传参通过引用传递,传进去的实际上是内存地址

  14. 如果已经创建了一个对象,并把另一个对象赋值给它,那么实际上创建了对另一个对象的引用,地址相同,注意这个时候不会调用拷贝构造函数

    赋值操作符,如果在声明时使用,那么调用拷贝构造函数;如果就是单纯的赋值操作,调用copy assignment function

  15. 编译器通过this指针决定使用哪个对象,而不是对象名

    如果一个对象的指针被创建,然后没有使用指针,对象就被销毁了,那么指针会变成野指针(dangling pointer)

  16. 为了防止指针指向的值发生变化,可以声明为const

  17. 对象数组,可以是相同值,必须是相同的类

    如果采用 Class_name arrayName[size]的方式声明,对象不能被单独初始化,必须在声明之后初始化所有对象

    如果只声明不初始化,该类必须有默认构造函数或零参数构造函数

    只有当该对象不需要数据的时候,只声明不初始化的对象数组才是有用的(这是啥啊,小狗摇头)

    如果构造函数重载了,对象数组中可以使用不同的构造函数进行初始化

  18. 对象的内存分配:对象成员的实际创建和内存分配

  19. new是type safe的,因为它不需要指定数据类型

    编译器会隐式转换 ::operator new::operatoe new(sizeof(type))

    e.g.: char (*pchar)[10] = new char[][10]

    new不能给引用分配内存,也不能分配函数,但是可以分配函数指针

    new出来的对象,即使超出作用范围,也不会销毁

  20. delete操作:deallocate一块内存,无返回值

    如果delete一块不是new出来的内存,会出现 unpredictable errors

    delete操作可以用于值为0的指针(nullptr),这意味着,如果new失败返回0,也可以用delete,避免出现未知错误

    delete是一个操作符,会调用delete操作符函数,如果在类外则调用全局delete操作符

  21. 拷贝构造函数,引用传参,如果是值传参编译器会给出 memory error

    创建临时实例,可以显式调用拷贝构造函数

    只要产生了临时对象,都会调用拷贝构造函数

  22. 一个不符合直觉的例子:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    class student{
    int marks;
    public:
    student() {}
    student(int x) { marks = x; }
    };
    main(){
    student s1(100);
    student s2();
    student s3 = 100;
    return 0;
    }

    编译器不会报错,能正常编译运行,但是s3不会被创建

    但是再看这个例子:

    1
    2
    3
    4
    5
    class student{
    int marks;
    }
    student s1;
    student s2=2;

    编译时错误,只有存在单个参数构造函数的时候,才能给s2赋值,在这里,只能在不使用参数的情况下构造或声明对象

    一道奇怪的题:we use static constructors to initialize the static members of class;静态构造函数没有参数

  23. 如果构造函数重载的时候,使用默认参数,那么可能用户本意是调用不同的构造函数,但传递的参数个数是相同的,这个时候编译器就不知道怎么办了

    所以不可以同时存在默认构造函数和全是默认参数的构造函数