结构体
声明形式:
struct 结构体名{
成员声明;
成员函数声明;
};
定义:
在c++允许声明结构体变量时省关键字struct(struct inflatable goose等价于inflatable goose)
成员访问修饰符:
- private
- protected
- public
说明:
- 结构体可以拥有成员函数,可以拥有析构函数和构造函数,但是默认访问权限是public的,用法和class相同
- 在结构体定义完后要加‘;’
- 引用结构体成员时,如果成员本身属于一个结构体,须一级一级找出最低级成员
- 结构体类似 类 ,只是类的默认成员访问修饰符是 private 而结构体是 public,结构体也可以继承与另一个结构体,
引用方式:
- 结构体变量名 . 成员名(或者成员函数名)
- 结构体变量指针 -> 成员名(或者成员函数名)
- (*结构体变量指针) . 成员名(或者成员函数名)
例子:
简单声明和访问结构体变量成员:
#include <iostream>
#include <string>
using namespace std;
struct person
{
string name;
bool sex;
int age;
person(string n,bool s,int a)
{
name = n;
sex = s;
age = a;
}
person()
{
name = "";
sex = 0;
age = 0;
}
void showinfo()
{
cout<<"名字为: "<<name<<"; 性别为: "<<(sex?"女":"男")<<"; 年龄为: "<<age<<endl;
}
};
void main()
{
person lisi("李四",0,18);
person *plisi = &lisi;
//结构体变量访问成员的方法:
lisi.showinfo();
//指针的两种访问成员的方法;
plisi->showinfo();
(*plisi).showinfo();
//修改结构体成员方法后的
lisi.age = 40;
lisi.showinfo();
system("pause");
}
/***************************************
输出结果:
名字为: 李四; 性别为: 男; 年龄为: 18
名字为: 李四; 性别为: 男; 年龄为: 18
名字为: 李四; 性别为: 男; 年龄为: 18
名字为: 李四; 性别为: 男; 年龄为: 40
请按任意键继续. . .
****************************************/
结构体成员为结构体的声明和访问其方法:
struct CPU
{
string type;
int price;
};
struct memory
{
string type;
int price;
};
struct mainboard
{
string type;
int price;
};
struct computer
{
// 结构体中声明结构体变量
CPU cpu;
mainboard mb;
memory my;
};
void func()
{
computer cmpu;
//对 cmpu 结构体中的 cpu, mb , my 结构体变量赋值
cmpu.cpu.price = 740;
cmpu.cpu.type = "Intel 酷睿i3";
cmpu.mb.price = 130;
cmpu.mb.type = "金士顿4GB";
cmpu.my.price = 500;
cmpu.my.type = "华硕P8H61";
}
结构体成员包含成员限制符:
struct ex
{
private:
string word;
public:
ex()
{
word = "hello!";
}
void show()
{
cout<<word<<endl;
}
};
void func()
{
ex ex1;
ex1.show();
ex1.word = "change the word"; //编译错误,因为 word 访问修饰符为 private
system("pause");
}
类
分类:
- private 私有变量和私有函数,其他的外部类,外部函数,派生类都无法访问这个部分的变量和函数,其只供给本类的成员函数调用和访问.
- public 公有变量和公有函数,所有外部函数,派生类,外部类,本类都可以访问这个部分的变量和函数
- protected 保护变量和保护函数,对于派生类来说此部分函数和变量是可以调用和访问的相当于是 public 权限,对于外部类和外部函数来说此部分是不能访问的相当于 private 权限
注意:
- 对于外部函数来说,protected与private访问权限一样
- 对于派生类来说,protected与public访问权限一样
例子:
// 例1 -- public, private ,protected 对于外部的区别 class A { private: int a; protected: int b; public: int c; }; void func() { A a; a.a = 5; //错误, 因为外部函数无法访问类的 private 成员 a.b = 5; //错误,因为外部函数无法访问类的 protected 成员 //由此看来对于外部函数来说, private 和 protected 的访问修饰符的都是禁止外部函数访问 a.c = 5; //而 public 访问修饰符则是允许外部访问 } // 例2 -- public, private ,protected 对于子类的区别 class A { private: int a; public: int b; protected: int c; }; class B:public A { void func() { a = 5;//错误, 因为子类无法访问类的 private 成员 b = 5;// protected 访问修饰符允许子类部访问 c = 5;// public 访问修饰符允许子类访问 //由此看来对于子类来说, public 和 protected 的访问修饰符的都是允许子类访问 } };
成员函数
[用户自定义成员函数(静态,非静态)][构造函数][析构函数][友元函数][虚函数][成员函数属性]
解释:
类的成员函数对于类产生的对象来说共用一组成员函数的,但各自有各自的成员变量
- 静态函数成员:
- 特点:
- 静态成员函数只能访问静态成员变量
- 在静态成员函数中是不能调用非静态函数和非静态成员数据变量
- 静态成员函数属于类本身,在类中加载的时候,即为他们分配了空间
- 静态成员和静态成员函数可以起到于全局变量和全局函数一样的作用,因此静态成员函数可以作为窗体编程的回调函数或多线程编程中线程的起始函数
- 声明形式(用static声明的成员函数):
- 访问修饰符 static 返回类型 函数名 (形参表);
- 访问方式:
- 类名::函数名(实参表)
- 实例名.函数名(实参表);
- 实例指针->函数名(实参表);
- 非静态成员函数:
- 特点:
- 属于类的实例,也就是应该先产生对象实例才可以调用类的非静态成员函数
- 非静态成员函数能访问静态成员变量与非静态成员变量
- 非静态的成员函数可以调用静态成员变量和非静态成员变量和静态函数
- 声明形式:
- 访问修饰符 返回类型 函数名(形参表);
- 访问方式:
- 实例名.函数名(实参表);
- 实例指针->函数名(实参表);
- 例子:
静态函数与非静态函数访问成员变量的区别:class A { private: int a; static int b; public: void changeValue() { a = 5; b = 10; } static void changeValue2() { a = 5; //错误,因为静态成员方法无法访问非静态成员变量 b = 10; } };
静态函数与非静态函数的调用区别:class A { public: void show() { cout<<"this is not static function!"<<endl; } static void show2() { cout<<"this is static function!"<<endl; } }; void main() { A a; A::show2(); A::show(); //错误,因为 A:: 后只能跟静态成员方法 a.show(); a.show2(); system("pause"); }
- 由用户定义: 由系统调用完成对对象的初始化,且在其完成工作前对象是不存在的
- 默认构造函数: 未提供显示初始化值时被用创建对象的构造函数
- 调用方式:
- 显示调用格式: 类名 对象名 = (形参列表);
- 隐示调用格式: 类名 对象名 (形参列表);
- 调用默认构造函数: 类名 对象名;
- 注意:
- 要创建对象而不显示地初始化则必须定义一个不接受任何参数的默认构造函数
- 在构造函数中使用new来初始化指针(指向对象),则应在析构函数中使用delete
- 如果有多个构造函数,则必须以相同的方式使用new,因为只有一个析构函数,不过可以在一个构造函数中使用new来初始化指令,而另一个构造函数中将该指令初始化为null或0,因为delete可作用与空指针
- 用new开辟对象, 格式:
- 类名 *指针名 = new 类名(形参表);
- 类名 *指针名 = new 类名(); //调用默认构造函数
- 例子:
// 例1 --- 调用构造函数 class A { private: int a; public: A(int b):a(b){} //这种形式等价于 A(int b){ a = b;} A(){ a = 5;}; void show(){cout<<"the value of a is: "<<a<<endl;} }; void func() { A ex0; // 调用默认构造函数 A(); A ex1(7); // 调用 A(int b)(); 这个构造函数 A ex2 = (8); // 调用 A(int b)(); 这个构造函数 ex0.show(); ex1.show(); ex2.show(); } /****************************************** 输出结果: the value of a is: 5 the value of a is: 7 the value of a is: 8 请按任意键继续. . . ******************************************/ // 例2 --- 动态开辟对象实例调用构造函数 class A { private: int a; public: A(int b):a(b){} //这种形式等价于 A(int b){ a = b;} A(){ a = 5;}; void show(){cout<<"the value of a is: "<<a<<endl;} }; void func() { A* ex0 = new A(); // 等价于 A* ex0 = new A; 调用默认构造函数 A(); A* ex1 = new A(7); // 调用 A(int b)(); 这个构造函数 ex0->show(); ex1->show(); } /****************************************** 输出结果: the value of a is: 5 the value of a is: 7 请按任意键继续. . . ******************************************/
- 定义: 由用户定义,却在对象生存周期即将结束时由系统自动调用
- 格式: ~ 类名();
- 说明: 析构函数无返回值,无声明类型,无参数
- 例子:
class A { private: int* a; public: A(){ a = new int(5);} //作用: 开辟一个整形变量 ~A(){delete a;} //作用: 释放一个动态开辟的整形变量内存 };
- 说明: 在类型声明中将原型放入并在原型声明前加上关键字friend,但不要在定义时加上关键字friend,它的访问权限与类的成员函数的访问权限相同<声明而已为private,public,protect>
- 作用: 把其他类中函数作为自己的友员成员函数,让特定的类成员成为另一个类的友元函数,而不必让整个类成为友元,但在使用时,必须小心排列各种声明和定义顺序(且需用到向前声明)
- 向前声明格式:
- class 类名 ;//向前声明本类
- class 所用到的友员成员函数的类的类名 {....};//友元函数类的定义
- class 类名 {…}//本类的定义
- 注意: 友元函数不要在类的内部定义,不然编译不会通过
- 例子:
友元函数:
//在类内部定义友元函数------------------------------------------------------- class Point { public: Point(double xx, double yy) { x=xx; y=yy; } friend void Getxy(Point &a){ cout<<"position is: ("<<a.x<<","<<a.y<<")"<<endl;} private: double x, y; }; //在类外部定义友元函数------------------------------------------------------- class Point { public: Point(double xx, double yy) { x=xx; y=yy; } friend void Getxy(Point &a); private: double x, y; }; void Getxy(Point &a) { cout<<"position is: ("<<a.x<<","<<a.y<<")"<<endl; }
友元成员函数:
class Date; //对 Date 类的提前引用声明 class Time //定义 Time 类 { public: Time(int,int,int); void display(Date&); //display 是 Time 类的成员函数,形参是 Date 类对象的引用 private: int hour; int minute; int sec; }; class Date { public: Date(int,int,int); friend void Time::display(Date&); //声明 Time 类的 display函数为本类的友元函数 private: int month; int day; int year; }; Time::Time(int h,int m,int s) //定义 Time 类的构造函数 { hour = h; minute = m; sec = s; } void Time::display(Date&d) //display 函数的作用是输出年、月、日和时、分、秒 { cout<<d.month<<"/"<<d.day<<"/"<<d.year<<endl; //引用 Date 类对象中的私有数据 cout<<hour<<":"<<minute<<":"<<sec<<endl; //引用本类对象中的私有数据 } Date::Date(int m,int d,int y) //定义 Date 类的构造函数 { month = m; day = d; year = y; } /* 请注意本程序的主函数中调用友元函数访问有关类的私有数据的方法: 1、在函数名 display 的前面要加 display 所在的对象名(t1); 2、display 成员函数的实参是 Date 类对象 d1,否则就不能访问 d1 中的私有数据; 3、在 Time::display 函数中引用 Date 类私有数据时必须加上对象名,如 d.month 。 */
- 作用: 如果想通过指向派生类对象的基类指针,调用派生类中覆盖的成员函数,可以用虚函数的方法让派生类中覆盖的成员函数被调用
- 格式: virtual 返回类型 函数名 (形参表);
- 注意事项:
- 如果定义的类作为基类,则应将那些要在派生类中重新定义的类的方式声明为虚拟的
- 如果使用指向对象的引用或指针调用虚方法,程序将使用做为对象类型的定义方法而不是使用做为了引用或指针定义的方法
- 工作原理:[详细介绍]
- 给每个对象添加一个隐藏成员,隐藏成员中保存了个指 向函数的地址数组的指针,这种数组称为虚函数表(vtbl)虚函数表中存储了为类对象进行声明的虚函数地址,派生类对象将包含一个指向独立地址的表指针,如果派生类提供了虚函数的新的定义,该虚函数地址被添加到vtbl中,注意,无论类中包含的虚函数是1还是10个都只需在对象中添加一个地址成员,只是表大小不同而已
- 纯虚函数:
- 格式: virtual 函数类型 函数名(形参表)=0;<无函数体>
- 作用: 有时,要在基类中不能给出虚函数实现代码,这是可将这种虚函数声明为纯虚函数
- 注意: 有纯虚函数的类都是抽象类,并且抽象类是不能产生实例的.
- 例子:
虚函数:
class A //有虚函数的基类 { public: virtual void show(){cout<<"这是基类的 A !"<<endl;} //用虚函数声明 show 函数 }; class B:public A { public: void show(){cout<<"这是派生类 B !"<<endl;} }; class C //没有虚函数的基类 { public: void show(){cout<<"这是基类 C !"<<endl;} }; class D:public C { public: void show(){cout<<"这是派生类 D !"<<endl;} }; void func( ) { A* a = new B; C* c = new D; a->show(); //通过虚函数找到子类的成员函数 show c->show(); //没有虚函数所以只能调用到基类的成员函数 show } /************************************ 调用 func 函数后的输出结果: 这是派生类 B ! 这是基类 C ! ************************************/
纯虚函数:
class A //有纯虚函数的基类 { public: virtual void show() = NULL; //用纯虚函数声明 show 函数 }; //要注意的是如果类有纯虚函数那么这个类无法产生实例,并且纯虚函数的必须在其的派生类中实现 class B:public A { public: void show(){cout<<"这是派生类 B !"<<endl;}//实现基类 A 的纯虚函数 show };
函数 能否被继承 成员还是友员 默认能否生成 能否为虚函数 是否可以有返回值
构造函数 × 成员 √ × ×
析构函数 × 成员 √ √ ×
= × 成员 √ √ √
& √ 任意 √ √ √
转换函数 √ 成员 × √ ×
() √ 成员 × √ √
[ ] √ 成员 × √ √
→ √ 成员 × √ √
op = √ 任意 × √ √
new √ 静态成员 × × void *
delete √ 静态成员 × × void
其他操作符 √ 任意 × √ √
其他成员 √ 成员 × √ √
友元 × 友员 × × √
静态成员:
- 无论对象有多少,其共用一份静态成员,其具有全局性
- 可以为private,public,protected,且需在外部声明并且没有this指针,在cpp中定义
- 静态成员变量属于类本身,在类中加载的时候,即为他们分配了空间,所以可以通过((类名.变量名)来访问.而.
非静态成员:
非静态成员变量属于类的实例,也就是应该先产生对象实例才会产生类的非静态成员变量
const成员:
只在调用构造函数时进行赋值,期间不能改动
枚举成员:
在类中声明一个枚举,在类声明中的这个枚举的作用域为整个类,因此可以用枚举为整形提供作用域为整个类的符号名称,用这种方式声明枚举并不会创建数据成员,也就是说,所以对象都不包含枚举成员
隐藏成员:
- this指针:
- 每个对象都有个this指针存放其地址,所以可以用*this引用对象,当成员函数的形参表中的参数与成员变量重名的时候,可以加以区分二者
- 虚函数表指针:
- 虚函数指针用于指向虚函数表,位于每个实例对象内存的头四字节,由于虚函数(Virtual Function)是通过一张虚函数表(Virtual Table)来实现的.简称为V-Table.在这个表中,主要是一个类的虚函数的地址表,这张表解决了继承、覆盖的问题,保证其真实反应实际的函数.这样,在有虚函数的类的实例中分配了指向这个表的指针的内存,所以,当用父类的指针来操作一个子类的时候,这张虚函数表就显得尤为重要了,它就像一个地图一样,指明了实际所应该调用的函数
例子:
成员变量:
class ex { private: enum {Len = 30}; //定义枚举成员 static const int Len2 = 30; //定义静态常量成员, 当然也效果等价于上面的枚举成员 Len char member[Len]; //定义非常量常成员 const int constMember; //定义常量成员 static int staticMember; //定义静态成员,若无显示的初始化,则其值为 0 public: ex(int j = 0,char *pmember = "null" ) :constMember(j) //对常量成员 constMember 的初始化 { for(int i=1;i<strlen(pmember);i++) member[i-1] = pmember[i-1]; } }; int ex::staticMember = 50; //对静态成员 staticMember 的显示初始化
this 指针使用:
class ex { private: int a; public: void changValue(int a)//此时函数参数 a 与成员变 a 重名 { this->a = a; //this 代表但前实例的地址,由此可以用此地址调用成员变量即 this->a ,以此区分函数参数 a 与成员变 a } };
格式:
返回类型 operater 操作符(成员列表)
限制:
- 不能违反操作符原来的语法规则 <例:不能将求模操作% 重载成使用一个操作数>
- 重载操作符不必是成员函数,但必须有一个操作数为用户定义类型
- 不能定义新的操作符
- 不能重载的操作符
Ⅰ sizeof 操作符;
Ⅱ . (成员操作符);
Ⅳ. *(成员指针操作符);
Ⅳ ::(作用解析操作符);
Ⅴ ?:(条件操作符);
Ⅵ typeid(一个RTTI操作符);
Ⅶ 强制转换操作符(const_cast,dynamic,_cast,reinterpret_cast,static_cast)- 可被重载的操作符
- 只能成员函数重载的操作符:
- = 赋值操作符
- () 函数调用操作符
- [] 下标操作符
- →∕→* 通过指针访问类成员操作符
例子:
class CMyString { private: char* m_str; public: CMyString(char *str) { int size; size = strlen(str) + 1; m_str= new char[size]; strcpy(m_str,str); } CMyString() { m_str = NULL; } ~CMyString() { delete []m_str; } void ShowString() { cout << m_str << endl; } CMyString & operator=(CMyString& mystring) //运算符重载函数 { int size; size = strlen(mystring.m_str) + 1; m_str = new char[size]; strcpy(m_str,mystring.m_str); return *this; } };
声明:
在类中用 friend class 友元类名;
作用:
在友元类的所有成员函数都可以访问此类中的所有数据
格式:
- class 友元类名 ;//向前声明本类
- class 拥有友元类的类名{....};//拥有友元类的类的定义
- class 友元类名 {…}//友元的定义
特点:
- 友元关系不能被继承
- 友元关系是单向的,不具有交换性.若类B是类A的友元,类A不一定是类B的友元,要看在类中是否有相应的声明.
- 友元关系不具有传递性.若类B是类A的友元,类C是B的友元,类C不一定是类A的友元,同样要看类中是否有相应的申明
例子:
使用友元类:
class B; //友元类的向前声明 class A //拥有友元类的类的定义 { int y; public: A() { y=4; } friend B; }; class B //友元类的定义 { public: void show(A &a) { cout<<a.y<<endl; } };
多个类包含一个友元类:
class C; //友元类的向前声明 class A //拥有友元类的类的定义 { int x; public: A() { x=4; } friend C; }; class B //拥有友元类的类的定义 { int y; public: B() { y=6; } friend C; }; class C //友元类的定义 { public: void show(A &a,B &b) { cout<<a.x<<" "<<b.y<<endl; } }; void func() { A a; B b; C c; c.show(a,b); }
一个类包含多个个友元类:
class B; //友元类向前声明 class C; //友元类向前声明 class A //拥有友元类的定义 { int x; public: A() { x=4; } friend C; friend B; }; class B //友元类的定义 { public: void show(A &a) { cout<<"the value of a is: "<<a.x<<" (in the class B)"<<endl; } }; class C //友元类的定义 { public: void show(A &a) { cout<<"the value of a is: "<<a.x<<" (in the class C)"<<endl; } }; void func() { A a; B b; C c; b.show(a); c.show(a); }
定义:
在类中声明了纯虚方法
注意:
- 抽象类可以有零个或多个抽象方法,也可以包含非抽象方法
- 抽象类可以没有抽象方法,但是有抽象方法的一定是抽象类
- 抽象方法只涉及声明不负责实现
- 抽象类可以派生出子类,子类的非抽象类必须抽象类的方法
- 抽象类不能创建对象
例子:
class A { public: virtual void show() const =0; };