c++中的类(class)-----笔记(类简介)
1, class 和 struct 都可以定义一个类,区别是两者在所支持的 默认信息隐藏方式不同:c++ 中默认为 private 类型,而 struct 中默认为 public 类型。
2,类的私有成员具有 类范围 性质,仅能由类的成员函数访问。
3,类成员函数的定义有两种方式:(a)在类声明的时候定义,就是在类内部定义(该方式称为 inline 方式)也可以在类中声明该函数时候加上 inline 关键字,强制转换为 inline 类型。(b)在类内声明,在类外定义,定义该函数时,需要使用 类解析符(:)。因为其它的类也可能会使用(定义)这些函数。
4,一般把类的声明放到一个单独的头文件中,这样当任何地方需要使用这个类的时候,通过 #include 宏就可以把类的声明包含进来;把类的定义单独放在一个文件中,例如 person.cpp,这种文件我们称为 实现文件。
5,除非迫不得已,一般来说应该采用引用方式进行对象的传递和返回,而不要采用传值的方式来进行。因为通过传值方式和返回对象时会降低效率并将面临对象间的拷贝操作,从而使数据增大,浪费内存。
6,一般来说,不要以引用方式在函数中返回一个 auto 局部变量。因为返回时,该对象已经不存在,调用者将引用一个不存在的对象而出错。
1 C& g() { 2 C c; 3 c.set(123); 4 return c; // Error: c goes out of existence when g exist; 5 }
7,const 的用法:(a)参数对象的引用中使用:通常来说,如果一个对象通过引用方式传到函数中,而函数 f 又不会改变该对象的状态,那么,最好将参数标记为 const,可以预防对参数的误写,有些编译器还可以对这种情况进行优化。(b)const 成员函数:如果一个成员函数不需要直接或间接来改变该函数所属对象的任何数据成员,最好把这个成员函数标记为 const。这种类型的函数称为只读函数。(c)const 类型的返回值:不允许通过引用来修改返回值。
1 class Emp { 2 public: 3 Emp() { 4 name = "alex"; 5 cout<<"default Create obj"<<endl; 6 } 7 Emp(const string& n) { // 不会改变 n 的状态 8 name = n; 9 cout<<"Create obj1"<<endl; 10 } 11 Emp(const char *name) { 12 this->name = name; 13 cout<<"Create obj2"<<endl; 14 } 15 private: 16 string name; 17 };
1 class B { 2 public: 3 void set(int n) { num = n; } 4 int get() const { return num; } // 不改变任何数据成员 5 private: 6 int num; 7 };
1 class B { 2 public: 3 const string& get() { return name;} // 不能通过引用修改返回值 name 4 };
8,构造函数:在调用它们时,不需要显式地提供函数名,编译器会自动调用它们。当我们创建一个类的实例时,就会自动调用它们。构造函数不能有返回类型,一个类可以有多个构造函数,也就是说,可以对构造函数进行重载,但每个构造函数必须拥有各不相同的函数署名。默认构造函数是不带任何参数的构造函数,其它构造函数称为带参构造函数。构造函数主要用来对数据成员进行初始化,并负责其它一些在对象创建时需要处理的事务。特点:函数名与类名相同,没有返回类型。构造函数既可以在类声明之中定义(inline),也可以在类声明之外定义。可以通过把默认构造函数进行私有化来对对象的创建进行约束。
9,拷贝构造函数:在带参构造函数中,有两类很重要的函数:拷贝构造函数和转型构造函数。拷贝构造函数创建一个新对象,此对象是另外一个对象的拷贝品,有两种原型:
Person (Person& ); // 第一种 Person (const Person& ); // 第二种
拷贝构造函数可以有多于一个的参数,但是第一个以后的所有参数都必须有默认值:
Person( const Person& p, bool married = false;)
note:通常,如果一个类包含指向动态存储空间的指针类型数据成员,则就应为这个类设计拷贝构造函数。拷贝构造函数设为私有的,可以禁止通过传值方式传递和返回类对象。
10,转型构造函数:类C转型构造函数用于类型间的转换,它只有一个参数。例如,类C的转型构造函数可将其他类型的变量转型为类C类型的变量。
1 class Person { 2 public: 3 Person() {name = "alex";} //default 4 explicit Person(const string& n) {name = n;} // 转型构造函数 5 Person(const char* n) {name = n;} // 转型构造函数 6 const string getName() {return name;} 7 private: 8 //Person(Person&); // 拷贝构造函数私有 9 string name; 10 };
Person 类拥有一个默认构造函数和两个转型构造函数,其中一个转型构造函数可将字符串常量转型为一个Person对象。
如果有这样的转型构造函数,那么编译器就在 string 对象 s 上调用该转型构造函数,以此来构造一个 Person 对象作为 函数的参数,我们称之为 隐式类型转换。但是这种方式可能会给我们带来无法预料的错误,用关键字 explicit 可帮我们实现这项功能。
11,构造函数初始化:对 const 类型的变量赋值是非法的。只要为函数添加一个初始化列表。初始化列表仅在构造函数中有效,不能用于其它函数。初始化列表是对 const 成员进行初始化的唯一方法。
1 class C { 2 public: 3 C() :c(0) { // 初始化列表对 const 成员进行赋值(唯一的办法) 4 x = 0; 5 // c = 0; Error: c is const 6 } 7 private: 8 int x; 9 const int c; // c 为 const 类型的变量,在声明的时候应该初始化,而且不能改变(不能被赋值) 10 };
note:数据成员的初始化顺序完全取决于它们在类当中声明的次序,与它们在初始化段中出现的次序无关。
1 class C { 2 public: 3 C() : C(0), x(-1){} // empty body 4 private: 5 int x; // x 的初始化先于 c,因为 x 声明先于 c 6 const int c; 7 };
12,析构函数:当对象被摧毁时,会自动调用析构函数。两种情况:(a)以某个类作为数据类型的变量超出了其作用范围(函数中定义的对象,当函数结束后被销毁);(b)用 delete 操作符删除动态分配的对象。析构函数不带参数,因此不能被重载,这样,每个类只能拥有一个析构函数。同样没有返回值。
13,类数据成员和类成员函数:到目前为止,我们看到的数据成员和成员函数都是属于对象的。但C++ 还支持另外一种类型的成员,这种成员属于类本身,而不属于类的对象,我们称之为类成员,而将属于对象的成员成为对象成员或实例成员。使用关键字 static 就可以创建一个类成员。
1 class C { 2 public: 3 // ... 4 private: 5 static unsigned int n; 6 }; 7 unsigned int C::n = 0; // 在程序块之外定义,注意时 C::n 不是 n
note: static 成员函数不会影响该类及其对象的 sizeof。
类成员函数:静态成员函数只能访问其它的 static 成员,包括数据成员和成员函数。访问类的 static 成员有两种方式,(a)通过类的对象来访问;(b)直接通过类 C 来访问(首选方法)。
note : 如果将成员函数内的某个局部变量定义为静态变量,该类的所有对象在调用这个成员函数时将共享这个变量。
1 class C{ 2 public: 3 void m(); 4 private: 5 int x; 6 }; 7 void C::m() { 8 static int s = 0; 9 cout<<++s<<endl; 10 } 11 12 int main() { 13 C c1,c2; 14 c1.m(); // outputs 1 15 c2.m(); // outputs 2 16 c1.m(); // outputs 3 17 18 return 0; 19 }
14,常量指针 this:在成员函数内部可以用指针常量 this 来访问与成员函数的调用相关联的对象(this 是一个关键字)。当传入的形参和类的成员函数相同时,通过 this 可以避免名称冲突。
1 class C { 2 public: 3 C() { this->x = 0;} // 调用相关联的对象 4 private: 5 int x; 6 }; 7 void setId (const string& id) { this->id = id;} // 避免名称冲突
note:this 指针是一个常量,它不能作为赋值,递增,递减等运算的目标对象。此外,this 只有在非 static 成员函数中才有效。