类和对象初步
面向对象的程序设计的基本特点:抽象,封装,继承,多态
类的定义:
class 类名{
访问范围说明符:
成员变量;
......
成员函数;
......
};
访问范围一共有三种:public,private,protected
成员变量、成员函数的写法与普通的变量、函数相同。
一个类的成员函数之间可以相互调用。类的成员函数可以重载 ,也可以设定函
数的默认值。
成员函数的定义可以在类外:
返回值类型 类名::函数名{
函数体;
}
所谓成员函数作用于某个对象上,指的是进入该成员函数时,函数中访问到的成
员变量是属于该对象的。
访问对象成员:
对象名.成员名
对象指针->成员名
和结构变量一样,对象之间可以使用"="相互赋值,但不能进行比较运算
#include <iostream> using namespace std; // 矩形类 class CRectangle{ public: int w, h; void init(int w_, int h_); // 设置宽度和高度 int area(); // 求面积 int perimeter(); // 求周长 }; void CRectangle::init(int w_, int h_){ w = w_; h = h_; } int CRectangle::area(){ return w*h; } int CRectangle::perimeter(){ return 2*(w + h); } int main() { int w, h; CRectangle r; cin >> w >> h; r.init(w, h); cout << "It's area is " << r.area() << endl; cout << "It's perimeter is " << r.perimeter() << endl; cout << sizeof(CRectangle) << endl; return 0; }
类成员的访问范围:
访问范围一共有三种:public,private,protected
public: 指定公有成员,一个类的公有成员在任何地方都可以被访问
private:用来指定私有成员,不论是成员变量还是成员函数,都只能在
该类的成员函数内部才能被访问
设置私有成员的机制叫做隐藏,隐藏的目的是强制对变量成员的访问
要通过成员函数进行。这样做的好处是如果以后修改了成员变量的类
型等属性,只需修改成员函数即可。否则需要修改所有访问成员变量
的语句。
隐藏机制可以避免对对象的不正确操作。
protected:用来指定保护成员
如果某个成员前面没有访问范围说明符,则对于class默认为私有成员
#include <iostream> #include <cstring> using namespace std; class CEmployee{ private: char szName[30]; public: int salary; void setName(char *name); void getName(char *name); void averageSalary(CEmployee e1, CEmployee e2); }; void CEmployee::setName(char *name){ strcpy(szName, name); } void CEmployee::getName(char *name){ strcpy(name, szName); } void CEmployee::averageSalary(CEmployee e1, CEmployee e2){ salary = (e1.salary + e2.salary) / 2; } int main() { CEmployee e; // strcpy(e.szName, "Tom"); error,不能访问私有成员 e.setName("Tom"); e.salary = 50000; return 0; }
构造函数:
面向对象的程序设计语言倾向于对象一定要经过初始化后,使用起来才比较安全,因此才引入构造函数
这一概念用于对对象进行自动初始化。
构造函数是一类特殊的成员函数,其名字和类的名字一样,不写返回值类型,可以重载。
如果类的设计者没有写构造函数,那么编译器会自动生成一个没有参数的构造函数。
无参构造函数,不论是编译器自动生成的还是程序员写的,都称为默认构造函数。
对象在生成时,一定会自动调用某个构造函数进行初始化,对象一旦生成,就再也不会在其上执行构造
函数。
#include <iostream> using namespace std; class Complex{ private: double real, imag; public: // 构造函数 Complex(double r); Complex(double r, double i); Complex(Complex c1, Complex c2); }; Complex::Complex(double r){ real = r; imag = 0; cout << "1" << endl; } Complex::Complex(double r, double i){ real = r; imag = i; cout << "2" << endl; } Complex::Complex(Complex c1, Complex c2){ real = c1.real + c2.real; imag = c1.imag + c2.imag; cout << "3" << endl; } int main() { Complex c1(3), c2(1, 2), c3(c1, c2), c4 = 7; return 0; }
复制构造函数:
复制构造函数时构造函数的一种,也称拷贝构造函数,它只有一个参数,参数类型是本类的引用。
如果类的设计者不写复制构造函数,编译器会自动生成复制构造函数,它的作用是实现从源对象到目标对象
逐个字节的复制。编译器自动生成的复制构造函数称为默认复制构造函数。默认构造函数不一定存在,但复
制构造函数总会存在。
复制构造函数的参数可以是const引用,也可以是非const引用。一般使用const引用,这样既可以使用常量作
为参数,也可以使用非常量对象作为参数去初始化其它对象。
复制构造函数被调用的三种情况:
1)用一个对象去初始化同类的另一个对象
2)作为形参的对象,用复制构造函数初始化的,而且调用复制构造函数时的参数,就是调用函数时所给的参数
3)作为函数返回值的对象是用复制构造函数初始化的,而调用复制构造函数时的实参,就是return语句返回的
对象
#include <iostream> using namespace std; class Complex{ public: double real, imag; Complex(double r, double i){ real = r; imag = i; } // 无复制构造函数 }; class Complex2{ public: double real, imag; Complex2(double r, double i){ real = r; imag = i; } Complex2(const Complex & c){ // 复制构造函数 real = c.real; imag = c.imag; cout << "Flag" << endl; } }; int main() { Complex c1(1, 2); // 调用构造函数初始化 Complex c2(c1); // 用默认复制构造函数初始化 Complex2 c_1(1, 2); Complex2 c_2(c_1); return 0; }
类型转换构造函数:
只有一个参数的构造函数一般都可以称作类型转换构造函数,因为这样的构造函数能够起到类型
自动转换的作用
析构函数:
成员函数的一种,它的名字与类名相同,但前面要加"~",没有参数和返回值。一个类有且仅有一个
析构函数。如果定义类时没有写析构函数,则编译器生成默认析构函数。
析构函数在对象消亡时自动被调用。可以定义析构函数在对象消亡后做善后工作。
函数的参数对象以及作为函数返回值的对象,在消亡时也会引发析构函数调用。
#include <iostream> using namespace std; class String{ private: char *p; public: String(int n); // 构造函数 ~String(); // 析构函数 }; String::String(int n){ p = new char[n]; } String::~String(){ // 利用析构函数释放所分配的动态内存 delete []p; cout << "Flag" << endl; // 用于测试的标记 } int main() { String array[2] = {2, 2}; String * pTest = new String(2); delete pTest; return 0; }
构造函数、析构函数和变量的生存周期
#include <iostream> using namespace std; class Demo{ private: int id; public: Demo(int i){ id = i; cout << "id = " << id << "constructed" << endl; } ~Demo(){ cout << "id = " << id << "destructed" << endl; } }; Demo d1(1); void Func(){ static Demo d2(2); Demo d3(3); cout << "func" << endl; } int main() { Demo d4(4); d4 = 6; cout << "main" << endl; Func(); cout << "main ends" << endl; return 0; } /* 输出结果: id = 1constructed id = 4constructed id = 6constructed id = 6destructed main id = 2constructed id = 3constructed func id = 3destructed main ends id = 6destructed id = 2destructed id = 1destructed */