面向对象编程概念:
面向对象编程概念的两项最主要特质是∶继承(inheritance)和多态(polymorphism)。前者使我们得以将一群相关的类组织起来,并让我们得以分享其间的共通数据和操作行为,后者让我们在这些类之上进行编程时,可以如同操控单一个体,而非相互独立的类,并赋予我们更多弹性来加入或移除任何特定类。
一般而言,class由两部分组成:一组公开的(public)操作函数和运算符以及一组私有的(private)实现细节。
class的声明以关键字class
开始,其后接一个class名称
class Stack;
class 定义由两部分组成:class的声明和紧接其后的主体。主体内的两个关键字public
和private
,用来标示每个块的member访问权限。
class Stack{
public:
//任何操作函数如果执行成功,就返回true
//pop和peek(查看)会将字符串内容置于elem内
bool push( const string& );
bool pop( string &elem );
bool peek( string &elem );
bool empty();
bool full();
//size()定义于class本身中,
//其他member这里只是声明
int size() {return _stack.size(); }
private:
vector _stack;
//习惯上在data member之前加下划线
};
所有member function都必须在class主体内进行声明。
如果要在class主体内定义,这个member function会自动被视为inline函数(比如上述的size()
)。
1)主体内定义:见上 size()
2)主体外定义
//两个冒号即类作用域解析运算符
bool Stack::empty()
{
return _stack.empty();
}
C++何时调用构造函数,何时调用析构函数
第一种初始化语法 :constructor的函数名称必须与class名称相同,不指定返回类型,亦不用返回任何值,可以被重载**:
class Triangular {
public:
//一组重载的constructor
Triangular(); //default constructors
Triangular(int len){ // 类内定义构造函数
_length = len;
_beg_pos = 1;
_next = 0;
};
Triangular(int len, int beg_pos): _length(len), _beg_pos(beg_pos) {
cout << "构造函数初始化列表" << endl;
};
void cout_elems()const {
cout << "_length:" << _length << "\n_beg_pos:" << _beg_pos << endl;
}
private:
int _next;
int _beg_pos;
int _length;
};
//默认的构造函数的定义方式1
//类主体外定义构造函数
Triangular::Triangular()
{
_length=1;
_beg_pos=1;
_next=0;
}
int main() {
Triangular t1; // 默认调用无参数构造函数
Triangular t2(5);
Triangular t3(5, 3);
t1.cout_elems();
t2.cout_elems();
t3.cout_elems();
return 0;
}
Triangular t1;
会对t1
应用default constructor(无需任何参数的constructor);
Triangular t2(10, 3);
会调用带有两个参数的构造函数,括号内的值被视为传给constructor的参数;
Triangular t3 = 8;
会调用带有单一参数的constructor,而不是赋值运算( ? 我推测是因为会重载运算符);
Triangular t5();
无法成功定义Triangular
对象,而是将t5
定义为一个函数,参数列表为空,返回Triangular
对象——因为C++兼容C,对C而言,t5
后带小括号会使其被视为函数。Triangular t5; // ok
成员初始化列表
是构造函数的第二种初始化语法,主要用于将参数传给member class object的constructor
,参数列表里保护传址符&
class Triangular {
public:
// ...
private:
int _next,_length,_beg_pos;
}
//紧接在参数列表最后的冒号后面,是个以逗号分隔的列表,
Triangular::Triangular(const Triangular &rhs) // & 传址,
: _length(rhs._length), // 欲赋值给member的数值被放在member名称后面的小括号中:
_beg_pos(rhs._beg_pos),
_next(rhs.beg_pos - 1)
{ } //对,这个大括号里是空的
//rhs是类对象,这里引用了类对象且不会对类对象做任何修改
析构函数是用户自定义的一个class member。在其类对象结束生命周期时,由析构函数处理善后这个类对象。**析构函数主要用来释放在**构造函数或类对象生命周期中分配的资源(构造函数初始化了数据成员(给了数据成员初值和内存)即为分配给数据成员资源!!)。
析构函数的名称有严格规定:class名称前加上~
前缀。绝对不会有返回值,也没有任何参数,其参数列表是空的,因而也不能被重载:
class Matrix {
public:
Matrix(int row, int col)
: _row(row), _col(col)
{
//构造函数进行资源的分配
_pmat = new double[row * col];
}
~Matrix()
{
//析构函数进行资源的释放
delete [] _pmat;
}
private:
int _row, _col;
double* _pmat;
};
//通过Matrix本身的构造函数和析构函数,完成了内存的自动管理
C++何时需要自定义析构函数呢?
1)如果本类中一个成员变量是别的对象的指针,而且这个指针不是传进来的地址而是这个指针指向的对象,是在本类中(如果是栈里的定位分配,也不用考虑内存)在堆中开辟的空间创建的。并且该指针没有进行过delete操作,那么久需要在析构方法中进行delete操作,此时我们就必须自己写析构函数 。
#include
using namespace std;
//日期类
class Date {
int year, month, day;
public:
//构造函数
Date(int y, int m, int d): year(y), month(m), day(d) {
cout << "调用了日期类的构造方法" << endl;
}
//对象销毁时 如果我们自己没有写析构方法,编译器会帮我们写一个然后调用
~Date() {
cout << "日期对象销毁了 ~Date()" << endl;
}
};
//员工类
class Employee {
string name;
Date *birthday;//声明一个指针 在构造方法里面让它指向一个对象
public:
//构造方法
Employee (string name): name(name) {
birthday = new Date(1989, 8, 8); //这里会调用日期的构造方法
cout << "调用了员工类的构造方法" << endl;
}
//析构方法
~Employee() {
cout << "员工对象销毁了" << endl;
}
};
int main() {
Employee em("假如我是张三");
return 0;
}
从运行结果可以知道创建的日期对象并没有销毁,所以有内存泄漏!
为什么泄漏了?因为出了mian函数,员工对象销毁,两个成员变量name和日期指针birthday在员工销毁的时候弹栈了,但是birthday指针指向的堆内存并没有销毁。所以应该在指针销毁之前释放指针指向的内存空间。
那么应该在哪里释放?员工对象在销毁的时候一定调用析构函数,所以在析构方法里对指针birthday进行delete,先把堆里开辟的内存空间清除掉,然后这个员工对象再销毁,所有内存才没有问题。
重写员工的析构函数如下:
~Employee() {
delete birthday;//清除指针指向的内存空间
cout << "员工对象销毁了" << endl;
}
2)全局区的对象在程序一开始就创建,程序结束才会销毁。栈区的对象在调用函数(代码块,如for循环里面)的时候才会创建,出了函数就会销毁。 在堆中开辟空间创建的对象必须我们自己手动delete。
重写main函数如下: (仔细看打印)
int main() {
Employee em("假如我是张三");
cout << "===== 1 栈里的对象 ======" << endl;
for (int i = 0; i < 3; i++) {
Date d(2015, 1, 23);
}
cout << "===== 2 ======" << endl;
{
Date d2(2015, 1, 2);
Date d3(2015, 1, 3);
Date d4(2015, 1, 5);
cout << "===== 3 ====" << endl;
}
cout << "=== 4 ====" << endl;
{
//这个代码块里会调用几次构造方法??0次
//因为这里只是创建指针并没有创建开辟空间对象
Date *d5;
Date *d6;
Date *d7;
}
cout << "=== 5 ====" << endl;
return 0;
}
Triangular tril1(8);
Triangular tril2 = tril2;
默认情形下,当我们以某个类对象作为另一个对象的初值时,class的成员对象会被依次复制。
注意:当有指针和地址类相关成员时,无法进行复制。如在Matrix类中,两个对象的_pmat
会指向同一个数组。当该数组空间被释放时,会出现严重的错误。
如何解决 ?可以通过copy constructor改变这种 成员逐一初始化 的行为模式
Matrix::Matrix(const Matrix &rhs)
: _row(rhs._row), _col(rhs._col)
{
//对rhs._pmat所指的数组产生一份完全复本
int elem_cnt = _row * _col;
_pmat = new double[elem_cnt];
for(int ix = 0;ix < elem_cnt;++ix)
_pmat[ix] = rhs._pmat[ix];
}
class Triangular {
public:
//以下是const member function
int length() const {return _length;}
int beg_pos() const {return _beg_pos;}
int elem(int pos) const;
//主体外定义时,
//必须同时在声明和定义中指定const
//以下是non-const member function
bool next(int &val);
void next_reset() { _next = _beg_pos - 1; }
//...
private:
int _length; //元素个数
int _beg_pos; //起始位置
int _next; //下一个迭代目标
static vector _elems;
};
//主体外,定义也要包含const
int Triangular::elem(int pos) const
{
return _elems[pos - 1];
}
(2)为什么会有在函数参表后加一个const呢?
为了确保类对象的值(内容)不会被更改,必须要确保成员函数都不会更改他们的调用者——类对象的值(内容),所以类设计者要在成员函数的参表后加一个const来告诉编译器:这个成员函数不会更改类对象的内容
(2)const修饰符紧接在函数参表之后,在class主体以外定义的成员函数,如果是一个const成员函数,那么就必须同时在声明与定义中指定const,编译器会检查每个声明为const的成员函数(函数参表后加了个const),检查他们是否真的更改了类对象的内容,如果更改了,编译器报错
class Triangular{
public:
//以下是const成员函数
int length() const{return _length;}//最好)和const中间加一个空格,不加也行。
int beg_pos() const{return _beg_pos;}
int elem(int pos) const;
//以下时非const成员函数
bool next(int &val);
void next_reaset()
{
_next=_beg_pos-1;
}
static vector_elems;//静态数据成员(以后说明)
int Triangular::elem(int pos)const//在这里指定const
{
return _elems[pos-1];
}
(3)注意,提防着标注着const的成员函数返回一个非const引用,引用(“指向”)标注着const的成员函数的函数体内的对象
虽然没有修改标注着const的成员函数的函数体内的对象,但是这样做实际上是将标注着const的成员函数的函数体内的对象开放了出去(因为可以通过非const的引用来修改类对象的内容),允许程序在其他地方加以修改,如:
/*这里,虽然我val()成员函数(参表为空,返回值是BigClass的非const引用)的参表后标着const,意思是val()这个成员函数不会更改类对象的内容,但是我返回的是一个非const的引用“指向”着_val。这不就是把_val开放了出去(因为可以通过非const的引用来修改_val,也就是类对象的内容),允许程序在其他地方可以修改_val,也就是类对象的内容了!
*/
class val_class{
public:
val_class(const BigClass &v)
:_val(v){}
//采用成员初始化列表的初始化语法(方式)
//来定义val_class类的构造函数
BigClass& val()const
{
return _val;
}
private:
BigClass _val;
};
如何解决呢?
我们知道成员函数可以根据其参表后加没加const而决定是否重载本身(成员函数)。所以我们提供const版本的和非const版本的成员函数的定义。如下:
class val_class{
public:
const BigClass& val()const{return _val;}
BigClass& val(){return _val;}
};
非const的类对象会调用非const的成员函数val(),const的类对象调用参表后标注着const的成员函数val()。如下:
void example(const BigClass *pbc,BigClass &rbc)
{
//为了区分const版本和非const版本,让这两个版本的比较直观一些
//所以参表这么设计,
//注意果const位于*的左侧,则const就是用来修饰指针所指向的变量,
//即指针指向为常量(该指针为函数指针,指向的函数参表是空的
//且返回类型也会带个const什么什么。(毕竟函数指针的数据类型
//要和它指向的函数的返回类型是一致的!))
//第二个参数是一个引用,引用非const的成员函数
pbc->val();//调用const版本的成员函数val()
rbc.val();//调用非const版本的成员函数val()
}
(1)关键字mutable
可以让我们做出这样的声明:对数据成员所做的改变并不会破坏类对象的常量性。先看下面这个例子:
class Triangular{
public:
bool next(int &val)const;
void next_reset()const
{
_next=beg_pos-1;
}
//...
private:
int _next;
int _beg_pos;
int _length;
};
int sum(const Triangular &trian)
{
if(!trian.length())
{
return 0;
}//类对象的长度这个数据成员是0的话,退出sum函数体
int val,sum=0;
trian.next_reset();
while(trian.next(val))
{
sum+=val;
}
return sum;
}
(2) 使用关键字mutable,
class Triangular{
public:
bool next(int &val)const;
void next_reset()const
{
_next=beg_pos-1;
}
//...
private:
mutable int _next;
int _beg_pos;
int _length;
};
现在next_reset()函数和next()函数既可以修改_next的值,又可以被声明为const成员函数了,这里面关键字mutable起的作用就是告知编译器数据成员(这里为私有成员)_next的值可变,就是让这个数据成员变得“特殊”,改变“mutable 数据类型 对象名”的数据成员(这里为私有成员)不会破坏类对象的常量性!
this指针,是一个能在成员函数里指向调用者(即类对象)的指针。我们需要一种可以指向整个类对象的指针,就是this指针。
欲以一个对象复制出另一个对象,先确定两个对象是否相同是一个好习惯(if(this != &rhs)
),这必然运用this指针。
Triangular& Triangular::copy(const Triangular &rhs)
{
//检查两个类对象是不是私有成员公共接口什么的相同不相同,
//不相同复制个屁。
if(this!=&rhs)
{
_length=rhs._length;
_beg_pos=rhs._beg_pos;
_next=_rhs._beg_pos-1;
}
return *this;//不管能不能复制,都返回this指针指向的调用者(用copy成员函数的类对象)
}
tr1.copy(tr2);//返回的由this指针所指的类对象,
//这句话就把tr2这个类对象作为了tr1这个类对象的初值
①我们可以在定义类成员函数的时候,用this指针去访问this指针指向的类对象的私有成员,利用这个特点从而去进行别的操作。
②我们可以在定义类成员函数的时候提领this指针来解决return谁的问题return *this;
#include
#include
using namespace std;
class Teacher {
public:
Teacher() {}
Teacher(int age, string name, int tel_nember): _age(age), _name(name), _tel_number(tel_nember) {}
Teacher ©(const Teacher &rhs) {
if (this != &rhs) {
_age = rhs._age;
_name = rhs._name;
_tel_number = rhs._tel_number;
}
}
void display_name() {
cout << this->_name << endl;
}
private:
int _age;
string _name;
int _tel_number;
};
int main() {
Teacher A(18, "A", 1234);
A.display_name();
A.copy(A); //调用的对象与自己相同
Teacher B;
B.copy(A); //实际是这样 copy(&B, A);
B.display_name();
return 0;
}
数据成员可以分静态变量、非静态变量两种
静态成员:静态类中的成员加入static修饰符,即是静态成员.可以直接使用类名+静态成员名访问此静态成员,因为静态成员存在于内存,非静态成员需要实例化才会分配内存,所以静态成员不能访问非静态的成员…因为静态成员存在于内存,所以非静态成员可以直接访问类中静态的成员.静态成员在每个类中只有一个拷贝,是解决同一个类的不同对象之间数据和函数共享问题的。
非成静态员:所有没有加Static的成员都是非静态成员,当类被实例化之后,可以通过实例化的类名进行访问…非静态成员的生存期决定于该类的生存期…而静态成员则不存在生存期的概念,因为静态成员始终驻留在内容中。
一个类中也可以包含静态成员和非静态成员,类中也包括静态构造函数和非静态构造函数。
malloc
堆内存。静态成员变量在对象中不占用存储空间,其实是放在全局变量空间里的。static
,类外实体无须加static
关键字,否则是错误的(因为在类外实体前面加static
会按照static
修饰全局函数类理解)。#include
#include
using namespace std;
class person
{
public:
int age; //普通成员变量
static int nums; //静态成员变量
static void print(void); // 静态成员函数
};
int person::nums = 1;
//static void person::print(void) 错误写法
void person::print(void) //正确写法
{
cout << "print()." << endl;
}
int main(){
person p1, p2;
cout << p1.nums << " " << p2.nums << endl;
p1.nums = 10;
cout << p1.nums << " " << p2.nums << endl;
person::print();
system("PAUSE");
return 0;
}
类名::静态成员变量
。int person::nums = 1;
int main(){
cout << person::nums << endl;
person::nums = 10;
cout << person::nums << endl;
system("PAUSE");
return 0;
}
static
,类外实体无须加static
关键字,否则是错误的(因为在类外实体前面加static
会按照static
修饰全局函数类理解)
- 1.直接访问
- 2.
this
指针访问- 3.
类名::func()
方式访问
class person
{
public:
int age; //普通成员变量
static int nums; //静态成员变量
static void print(void);
void func(void)
{
//print(); 方法1
//this->print();方法2
person::print();//方法3
}
};
void person::print(void)
{
cout<<"static function print()."<
class person
{
public:
int age; //普通成员变量
static int nums; //静态成员变量
static void print(void);
};
void person::print(void)
{
this->age=10; // 报错
}
#include
#include
#include
#include
using namespace std;
class Myclass {
public:
Myclass() {
m = 10;
};
static int getn(Myclass a);// 静态成员函数
static int z; // 公共静态数据成员
private:
int m; // 非静态数据成员
static int n; // 静态数据成员
};
int Myclass::n = 100; // 静态数据成员初始化
int Myclass::z = 50; // 公共静态数据成员初始化
int Myclass::getn(Myclass a) {
// cout << m << endl; //对x的引用是错误的
cout << a.m << endl; // 通过类间接使用 非静态数据成员,正确的
cout << n << endl; // 正确的
cout << a.n << endl; // 正确的
return n; // 直接使用 静态数据成员
}
int main() {
Myclass app1;
cout << app1.getn(app1) << endl; // 利用对象引用静态函数成员
cout << Myclass::getn(app1) << endl; // 利用类名引用静态函数成员
cout << Myclass::z << endl;
}
运算符函数很像普通函数,但是运算符函数不用指定函数名。只需要在运算符前加上关键字operator即
运算符重载的规则如下:
.
、.*
、::
、?
其他运算符皆可被重载;typedef
为某个类型设定另一个不同的名称:typedef existing_type new_name;
,其中的existing_type
可以是任何内置类型、复合类型或class类型。
typedef 内置类型/复合类型/class类型 new_name;
//new_name是该类型的别名
C++typedef的详细用法
同类无私处,异类有友元
在 C++中,一个类中可以有 public、protected、private 三种属性的成员,通过对象可以访问 public 成员,只有本类中的函数可以访问本类的 private 成员。借助友元(friend),可以使得其他类中的成员函数以及全局范围内的函数访问当前类的 private 成员。
声明函数时,在前面加friend
关键字,这样就构成了友元函数。友元函数可以是不属于任何类的非成员函数,也可以是其他类的成员函数。
friend 返回类型 函数名(参数表);
#include
using namespace std;
class Student {
public:
Student(char *name, int age, float score);
friend void show(Student *p); //将show声明为友元函数
private:
char *m_name;
int m_age;
float m_score;
};
// 构造函数:成员逐一初始化操作
Student::Student(char *name, int age, float score): m_name(name), m_age(age), m_score(score) {}
// 非成员函数
void show(Student *p) {
//调用了private 成员,原则上不能通过对象访问,友元函数可以访问。将第7行注释掉,观察编译器的报错信息。
cout << p->m_name << "的年龄是:" << p->m_age << ",成绩是:" << p->m_score << endl;
}
int main() {
Student stu("小明", 15, 90.6);
show(&stu); //调用友元函数
Student *pstu = new Student("李磊", 16, 80.5);
show(pstu); //调用友元函数
return 0;
}
注意,友元函数不同于类的成员函数,在友元函数中不能直接访问类的成员,必须要借助对象。下面的写法是错误的:
void show(){
cout<
成员函数在调用时会隐式地增加 this 指针,指向调用它的对象,从而使用该对象的成员;而 show() 是非成员函数,没有 this 指针,编译器不知道使用哪个对象的成员,要想明确这一点,就必须通过参数传递对象(可以直接传递对象,也可以传递对象指针或对象引用),并在访问成员时指明对象。
#include
using namespace std;
class Address; //提前声明Address类
//声明Student类中的成员
class Student {
public:
Student(char *name, int age, float score);
void show(Address *addr); // Student类中的成员函数
private:
char *m_name;
int m_age;
float m_score;
};
//声明Address类中的成员
class Address {
private:
char *m_province; //省份
char *m_city; //城市
char *m_district; //区(市区)
public:
Address(char *province, char *city, char *district);
//将 Student 类的成员函数 show() 声明为 Address 类的友元函数,show() 就可以访问 Address 类的 private 成员变量了
friend void Student::show(Address *addr);
};
//实现Student类
Student::Student(char *name, int age, float score): m_name(name), m_age(age), m_score(score) { }
void Student::show(Address *addr) {
// Student类的成员函数可以直接防御类的成员变量
cout << m_name << "的年龄是 " << m_age << ",成绩是 " << m_score << endl;
// 声明了友元函数,可以通过对象调用类的成员变量
cout << "家庭住址:" << addr->m_province << "省" << addr->m_city << "市" << addr->m_district << "区" << endl;
}
//实现Address类
Address::Address(char *province, char *city, char *district) {
m_province = province;
m_city = city;
m_district = district;
}
int main() {
//第一种类的new和调用
Student stu("小明", 16, 95.5f);
Address addr("陕西", "西安", "雁塔");
stu.show(&addr);
//第二种类的new和调用
Student *pstu = new Student("李磊", 16, 80.5);
Address *paddr = new Address("河北", "衡水", "桃城");
pstu -> show(paddr);
return 0;
}
注意: 一个函数可以被多个类声明为友元函数,这样就可以访问多个类中的 private 成员。
类与类之间也可以建立friend关系,比如class A与class B去建立关系,这样A类里的所有成员函数都成为了B类的friend,同理B类里的所有成员函数都成为A类的friend。
例如将类 B 声明为类 A 的友元类,那么类 B 中的所有成员函数都是类 A 的友元函数,可以访问类 A 的所有成员,包括 public、protected、private 属性的。
class A {
public:
friend class B;
private:
...
}
更改上例的代码,将 Student 类声明为 Address 类的友元类:
#include
using namespace std;
class Address; //提前声明Address类
//声明Student类
class Student {
public:
Student(char *name, int age, float score);
void show(Address *addr);
private:
char *m_name;
int m_age;
float m_score;
};
//声明Address类
class Address {
public:
Address(char *province, char *city, char *district);
//将Student类声明为Address类的友元类
friend class Student;
private:
char *m_province; //省份
char *m_city; //城市
char *m_district; //区(市区)
};
//实现Student类
Student::Student(char *name, int age, float score): m_name(name), m_age(age), m_score(score) { }
void Student::show(Address *addr) {
cout << m_name << "的年龄是 " << m_age << ",成绩是 " << m_score << endl;
cout << "家庭住址:" << addr->m_province << "省" << addr->m_city << "市" << addr->m_district << "区" << endl;
}
//实现Address类
Address::Address(char *province, char *city, char *district) {
m_province = province;
m_city = city;
m_district = district;
}
int main() {
Student stu("小明", 16, 95.5f);
Address addr("陕西", "西安", "雁塔");
stu.show(&addr);
Student *pstu = new Student("李磊", 16, 80.5);
Address *paddr = new Address("河北", "衡水", "桃城");
pstu -> show(paddr);
return 0;
}
friend
,就可以将它声明为某个class的friend。这份声明可以出现在class定义的任意位置(不受private或public的影响)。对于一个类,里面包含了一个指针成员(私有成员),然而这个类我们声明两个对象,然后一个对象给另一个对象赋值,采用默认的成员逐一复制操作的话(即前者=后者;
)就会让两个类的指针成员都指向一个堆里的动态数组,要是这两个类对象的析构函数都作用的话,前者析构对象作用后,delete掉了这个堆里的动态数组,那么,后者的指针成员就成了个野指针,显然这种方法就不对。
所以需要一个拷贝构造函数和拷贝赋值运算符,重新定义=
Matrix& Matirx::
operator=(const Matrix &rhs)//很像重载运算符一样
{
if(this!=&rhs)//这里this指针指代"="左边的类对象。
{//要是类对象间赋值(复制=右边的类对象所有public和private成员给
//=左边的类对象),如果俩类对象都是一样的,那你=还有什么意义?
//浪费时间(直接跳过if)。
_row=rhs._row;
_col=rhs._col;
int elem_cnt=_row*_col;
delete []_pmat;
_pmat=new double[elem_cnt];
//至于delete到new这两行,我的理解就是解除因=左边这个类对象
//的构造函数让其指针成员指向了=右边的类对象里的new出来的数组
//的这个指向。然后我让其指针成员指向自己new出来的数组,然后
//给这个数组的所有元素赋值=右边的类对象new出来的数组的所有元素就可以了。
//卧槽虽然看起来不像这个意思但是作者是这么想的。
for(int ix=0;ix
//LessThan.h文件
#ifndef _LESSTHAN_H_
#define _LESSTHAN_H_
#include
#include
using namespace std;
class LessThan {
public:
LessThan(int val): _val(val) {}; //采用成员初始化列表语法形式的LEssThan类构造函数
int comp_val()const {
return _val;//因为这个类的每个类对象在定义的时候都必须提供一个参照数(你跟谁比,比它小还是大的怎么怎么着)(comp_val),因此这是参照数的读取
}
void comp_val(int nval) {
_val = nval; //参照数的写入,这是一个重载函数(comp_val重载函数)
}
bool operator()(int value)const;//运算符函数的声明,供LessThan(参数)这样的()运算的表达式使用。定义放在程序代码文件里。
private:
int _val;//供public定义的函数用。
};
int count_less_than(const vector &vec, int comp);
void print_less_than(const vector &vec, int comp, ostream &os = cout);
//在LessThan.h所附属的.cpp文件里的两个非成员函数的声明在此
#endif // _LESSTHAN_H_
//LessThan.cpp文件
#include "LessThan.h"
#include
#include
#include
using namespace std;
inline bool LessThan::
operator()(int value)const { //最好函数声明和定义的参数表的参数名一致。
return value < _val; //小于return true,否则return false
}
//函数调用运算符的实现。然而重载运算符的运算符函数的定义必须和用到的重载运算符的
//地方或函数要放在一个文件里,否则编译器报错
int count_less_than(const vector &vec, int comp) {
//从vector容器里挑出比某个参照数小的元素,计数有多少个这样的元素
LessThan lt(comp);//定义LessThan类对象。(就像LessThan lt10(10);)
//(和定义一般对象一样的定义方式,只是这个类对象需要有一个参照数(初始值,即类对象定义的时候就被初始化了)
int count = 0;
for (int ix = 0; ix < vec.size(); ix++) {
if (lt( vec[ix] )) //()运算符重载(针对了lt这个类对象在()前的形式的重载),所以这个就是个判断vector容器里的某个元素是否比comp这个参照数小,
//这里提示了undefined reference to `LessThan::operator()(int) const'|||error: ld returned 1 exit status|
//小的话if(true),否则if(false)
{
++count;//比参照数小,计入count里
}//否则什么也不做
}
return count;//返回有多少个比参照数小的元素个数
}
void print_less_than(const vector &vec, int comp,
ostream &os) //②默认值**只能指定一次**,在函数声明或定义,不能全给指定了,这是
//默认参数值的提供的两个规则之一,另一个是①默认值的解析操作从最右边开始进行,
//也就是说我们给某个参数提供了默认值则其右边的所有参数必须拥有默认值!(该规则主要体现在函数声明或定义中)
//否则编译器报错
{
LessThan lt(comp);//定义LessThan类对象。(就像LessThan lt10(10);)
//(和定义一般对象一样的定义方式,只是这个类对象需要有一个参照数(初始值,即类对象定义的时候就被初始化了)
vector::const_iterator iter = vec.begin(),
it_end = vec.end(); //常量型泛型指针的定义(常量型泛型指针不会改变指向的容器的内容,
//同时这么写会告诉程序读者我不会改变泛型指针指向的容器的内容。
os << "elemts less than " << lt.comp_val() << endl; //调用类对象里的public(公共)成员函数
while ((iter = find_if(iter, it_end,
lt)) != it_end) //找出第一个比lt类对象里的参照数小的元素(关于find_if()这个泛型算法的函数原型请查阅资料)
//那个()注意不要括错了,会有
//error: no match for 'operator=' (operand types are 'std::vector::const_iterator {aka __gnu_cxx::__normal_iterator >}' and 'bool')|
//这样的报错
//资料:(注意,用了泛型算法,得包含#include !!!!!!!!!!
/** template
InputIterator find_if ( InputIterator first, InputIterator last, Predicate pred )
{
for ( ; first!=last ; first++ ) if ( pred(*first) ) break;
return first;
}
*/
//它在区间[first,end)中搜寻使一元判断式pred为true的第一个元素。如果没找到,返回end。
//通常我们把函数对象当作参数传给泛型算法,就体现在了find_if()这一行。
{
os << *iter << ' '; //输出小于参照数的vector容器的元素。
++iter;//泛型指针迭代,辅助输出符合条件的元素
}
}
//main.cpp文件
#include
#include
#include "LessThan.h"
#include
using namespace std;
int main() { //测试。
int ia[16] = {17, 12, 44, 9, 18, 45, 6, 14, 23, 67, 9, 0, 27, 55, 8, 16};
vectorvec(ia, ia + 16);
//vector容器的建立和初始化
int comp_val = 20; //参照数的设置
cout << "Numeber of elements less than " << comp_val << " are " << count_less_than(vec, comp_val) << endl;
//小于参照数的vector容器里的元素有多少个
print_less_than(vec, comp_val); //输出上述的这些个元素
return 0;
}
为什么有要重载iostream运算符?
因为我们想对某个类对象进行读写操作。不重载iostream类运算符直接cout<<类对象<
cin>>类对象;
编译器会报错
重载运算符(operator)
直接cout<<类对象<
ostream& operator<<(ostream &os,const Triangular &rhs)
{
os<<"("<
istream& operator>>(istream &is,Triangular &rhs)
{
char ch1,ch2;//定义两个字符用来规定
//>>运算符后的输入格式,像scanf函数里可规定输入格式那样
int bp,len;//起始位置和数列长度的定义
is>>ch1>>bp>>ch2>>len;
//假设输入为(3,6) 6 10 15 21 28 36
//那么ch1=='(',bp==3,ch2==',',len==6。
rhs.beg)pos(bp);
rhs.length(len);
rhs.next_reset();
//输入的数据对应传给传进来的类对象(rhs)的数据成员。
return is;
//传入重载运算符函数的istream对象又被原封不动地返回
}
类名::*pointer = &类名::变量名
如:string Student::*pstr1 = &Student::name;
#include
#include
using namespace std;
class Student {
public:
Student(string n, int num): name{n}, num{num} {} // 构造函数:成员初始化列表
void display(int idx) {
cout << idx << " " << name << " " << num << endl;
}
public:
string name;
int num;
};
int main() {
Student s1("majin", 100);
Student *ps1 = &s1;
Student s2("moqun", 80);
Student *ps2 = &s2;
string Student::*pstr1 = &Student::name;
cout << s1.*pstr1 << endl; //不同的对象可以调用同一个指针
cout << ps1->*pstr1 << endl;
cout << s2.*pstr1 << endl;
cout << ps2->*pstr1 << endl;
return 0;
}
(类名::*ptr)(函数参数) = &类名:: 成员函数
如 void (Student::*pdis)(int) = &Student::dis;
下面看成员函数指针的案例:
// 这个案例中,我们不想让外界知道类内部的函数名,我们可以是用指向成员函数的指针数组将它们封装起来,加强了隐蔽性
#include
using namespace std;
class Game
{
public:
Game(){
PSkill[0] = &Game::SkillOne;
PSkill[1] = &Game::SkillTwo;
PSkill[2] = &Game::SkillThree;
PSkill[3] = &Game::SkillFour;
}
void select(int index) {
if (index >= 0 && index <= 3) {
(this->*PSkill[index])();
}
}
private:
void SkillOne(){ cout << "Use skill one.." << endl; }
void SkillTwo(){ cout << "Use skill Two.." << endl; }
void SkillThree(){ cout << "Use skill Three.." << endl; }
void SkillFour(){ cout << "Use skill Four.." << endl; }
enum {
NC = 4 //技能数量
};
void (Game::*PSkill[NC])(); //函数数组
};
int main(){
Game newOne;
newOne.select(2);
newOne.select(0);
newOne.select(3);
system("PAUSE");
return 0;
}
私有成员只能成员函数调用或者友元类里访问
带有构造函数和析构函数的类对象被定义后,编译器自动调用构造函数为其初始化,程序执行到其生命周期结束编译器自动调用析构函数释放其所占的内存
析构函数
,因为构造函数和析构函数定义内部产生出来的对象都在堆内存里。~构造函数名()
没有参数,没有返回值。class
内用static
修饰成员变量,则被修饰的成员变量即为静态成员变量,用static
修饰成员方法,则被修饰的成员方法即为静态成员方法。