类和对象
-
封装和抽象的不同之处:前者binding,后者hiding,通过封装可以实现抽象
-
类没有size一说
类对象有size,取决于all data members
-
如果一个类的成员都是私有的,它的隐式构造函数也还是要使用public说明符
在不能创建实例的类中,构造函数需要用private说明
简单来说就是,类外创建实例,只能通过public的构造函数创建
-
构造函数是private的,只能在父类中创建实例,不能继承
-
构造函数是protected的,还可以在子类中创建实例
-
构造函数是public的,可以在程序中任何地方创建实例
-
-
私有的成员函数不能被重写(override),但是可以被重载(overload)
-
对于这段代码,每一次当对象被创建的时候,z将会被使用,但x和y不一定会
1 2 3 4 5
class A{ int x, y, z; public: A(){ y = 100; x = 100 * y; } }
-
类的成员变量中,被声明为private的是最安全的,不是protected
但是吧,又说,protected的成员变量和private的一样安全,而且可以被继承
class中,默认成员是private的,如果主动声明为protected,就可以在包外通过继承访问成员,否则private包外访问不了
-
静态成员遵循类成员访问规则(public, private, protected)
静态成员函数不和任何对象关联,当调用静态成员函数时,也没有this指针
静态成员函数不能是virtual,const,volatile的
-
数据成员只能用构造函数初始化,但静态成员变量必须在类外初始化 (确保其唯一确定性)
因为静态成员变量属于整个类,而不是类的特定实例,而普通的实例变量是每个类的实例特有的,因此可以在构造函数或原位初始化
-
member function:属于类的函数
将其概括为“在类中定义的函数”是不准确的,因为通过继承得到的函数,并不在类中定义,但一旦继承就会属于该类
成员函数有五种类型:simple,static,const,inline,friend
常量成员函数:不改变调用对象值的函数,当返回值是成员变量的引用的时候,不能const修饰;但如果是成员外变量的引用,是可以用const修饰的
-
Properties of an object: properties, identity, attributes
names不是
-
对象是类的实例,程序运行期间对象的类型一经确定无法改变
当且仅当构造函数被调用之后,操作系统才会给这个对象分配内存空间,大小取决于data members,但不包括静态成员变量
两个对象可以指向相同的内存位置
单例对象:程序运行期间,该类只有这一个实例
-
当函数返回一个对象时,会隐式地创建一个临时对象,然后把值赋给临时对象(把变量值逐一拷贝过去),并返回这个临时对象
局部变量不能通过引用返回,否则会compile time error
因为引用返回,实际上是让对象指向这个值的内存地址,然而局部变量直接销毁了,指不过去的
-
如果函数传参直接传对象,那么拷贝构造函数将会被调用,把该对象复制给函数的局部变量中
如果函数传参想通过对象传数据成员,要求该数据成员是public的而且它是被传递给类外函数的
如果函数传参通过引用传递,传进去的实际上是内存地址
-
如果已经创建了一个对象,并把另一个对象赋值给它,那么实际上创建了对另一个对象的引用,地址相同,注意这个时候不会调用拷贝构造函数
赋值操作符,如果在声明时使用,那么调用拷贝构造函数;如果就是单纯的赋值操作,调用copy assignment function
-
编译器通过this指针决定使用哪个对象,而不是对象名
如果一个对象的指针被创建,然后没有使用指针,对象就被销毁了,那么指针会变成野指针(dangling pointer)
-
为了防止指针指向的值发生变化,可以声明为const
-
对象数组,可以是相同值,必须是相同的类
如果采用
Class_name arrayName[size]
的方式声明,对象不能被单独初始化,必须在声明之后初始化所有对象如果只声明不初始化,该类必须有默认构造函数或零参数构造函数
只有当该对象不需要数据的时候,只声明不初始化的对象数组才是有用的(这是啥啊,小狗摇头)
如果构造函数重载了,对象数组中可以使用不同的构造函数进行初始化
-
对象的内存分配:对象成员的实际创建和内存分配
-
new是type safe的,因为它不需要指定数据类型
编译器会隐式转换
::operator new
为::operatoe new(sizeof(type))
e.g.:
char (*pchar)[10] = new char[][10]
new不能给引用分配内存,也不能分配函数,但是可以分配函数指针
new出来的对象,即使超出作用范围,也不会销毁
-
delete操作:deallocate一块内存,无返回值
如果delete一块不是new出来的内存,会出现
unpredictable errors
delete操作可以用于值为0的指针(nullptr),这意味着,如果new失败返回0,也可以用delete,避免出现未知错误
delete是一个操作符,会调用delete操作符函数,如果在类外则调用全局delete操作符
-
拷贝构造函数,引用传参,如果是值传参编译器会给出
memory error
创建临时实例,可以显式调用拷贝构造函数
只要产生了临时对象,都会调用拷贝构造函数
-
一个不符合直觉的例子:
编译器不会报错,能正常编译运行,但是s3不会被创建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; }
但是再看这个例子:
编译时错误,只有存在单个参数构造函数的时候,才能给s2赋值,在这里,只能在不使用参数的情况下构造或声明对象1 2 3 4 5
class student{ int marks; } student s1; student s2=2;
一道奇怪的题:we use static constructors to initialize the static members of class;静态构造函数没有参数
-
如果构造函数重载的时候,使用默认参数,那么可能用户本意是调用不同的构造函数,但传递的参数个数是相同的,这个时候编译器就不知道怎么办了
所以不可以同时存在默认构造函数和全是默认参数的构造函数