- [基本数据结构]()
- [类的使用]()
- [类的继承和派生]()
- [面向对象特征之一——多态]()
- [操作符重载]()
- [const关键字的使用]()
1. 类的定义与使用
1.1 类的定义
最简单的类的申明:
class Student{
};
在类的定义最后需要加上分号,这是和java不同的地方,否则会有编译错误。
1.2 类的使用
即创建类的实例——对象。在java中你会使用到new
关键字,而在c++中,创建对象与使用普通的数据类型一样简单:
Student studentObject;
2. 类的成员变量和成员函数
2.1 成员变量
class Student
{
char name[20]; //姓名
int id_num; //学号
int age; //年龄
char sex; //性别
和java不同,类的成员变量不能在创建的时候初始化值。
2.2 成员函数
和java不同,c++的成员函数有两种定义方式:
1.类内部申明,类外定义(实现):
class student
{
void set_age(int a);
int get_age();
};
//在类外部定义set_age函数
void student::set_age(int a)
{
age = a;
}
//在类外部定义get_age函数
int student::get_age()
{
return age;
}
在类内部申明函数,在类的外部定义的时候通过作用域操作符 :: 进行实现。
申明的时候,只需写出返回类型、函数名称、参数列表、末尾分号即可,不能加上空的大括号。
返回类型 类名::成员函数名(参数列表)
{
//函数体
}
你也可以在类的成员函数申明的前面加上inline
关键字使其变成内联函数。内联函数可以优化程序的执行效率。
class student
{
inline void set_age(int a);
inline int get_age();
};
//在类外部定义set_age函数
void student::set_age(int a)
{
age = a;
}
//在类外部定义get_age函数
int student::get_age()
{
return age;
}
2. 类内部边申明边定义(实现)
class Student{
void set_age(int a){age = a;}
int get_age(){return age;}
}
这种方式其实和java的成员函数调用方式是相同的。
2.3 访问限制
和java的语法类似,c++也提供了三种关键字:
-
public
:本类和其他类都可以访问到 -
private
: 只有本类可以访问到 -
protected
:只有本类和派生的类可以访问。
但是使用的语法格式有点不同。
class book
{
public:
void setprice(double a);
double getprice();
private:
double price;
};
通过这种格式,可以将属性为public
的变量或函数申明在一起。避免了重复的写权限限制符号。
当然,权限相同的成员也可以分开声明,申明的顺序也是无所谓的。
2.4 访问成员变量/函数
创建的对象就是类的实例,就直接使用.
点选择符,这里是和java一样的。
创建的如果是对象的指针,需要使用->
箭头选择符访问成员。java中没有指针就没有这条规则。
#include <iostream>
using namespace std;
class book
{
public:
void setprice(double a);
double getprice();
private:
double price;
};
void book::setprice(double a)
{
price = a;
}
double book::getprice()
{
return price;
}
int main()
{
book Alice;
Alice.setprice(29.9);
cout<<"The price of Alice is $"<<Alice.getprice()<<endl;
book *Harry = new book;
Harry->setprice(49.9);
cout<<"The price of Harry is $"<<Harry->getprice()<<endl;
return 0;
}
注意:C++类class和结构体struct区别
class和struct都可以定义一个类的结构。
- 采用struct关键字,结构体中定义的成员变量或成员函数默认都是public属性的。
- 使用class关键字,类中定义的成员变量或成员函数默认都是private属性的,
除此之外,没有其他任何区别。
3. 构造函数
3.1 构造函数的定义
构造函数的声明与定义的方式与普通函数是相同的,也是有两种方式,但是有以下几点特殊地方:
- 构造函数的函数名必须与类名相同;
- 构造函数无返回值;
- 当我们创建类对象的时候构造函数会被自动调用,而无需我们主动调用。
这三点和java中语法也是一样的。
通常如果在定义类的时候,没有定义任何一个构造函数的时候,系统会自动生成一个默认构造函数。默认构造函数就是不带任何参数的构造函数。其它带参数的构造函数统称为带参构造函数。
如果在类中声明了任何一个构造函数,则系统不会自动生成默认构造函数。
如果在创建对象时候表示使用哪个构造函数呢?只要在对象名称后加一对小括号,小括号类参数与对应的构造函数参数对应即可:
class book
{
public:
book(){}
book(char* a, double p);
private:
double price;
char[] title;
};
book::book(char[] a, double p)
{
title = a;
price = p;
}
int main()
{
book Harry("Harry Potter", 49.9);
return 0;
}
3.2 初始化参数表
除了通过在构造函数的函数体内进行成员变量的初始化,还可以通过在构造函数的后面以下面的格式初始化参数:
构造函数(参数列表):成员变量1(初始值),成员变量2(初始值)……{}
举个例子:
class book
{
public:
book(){}
book(char *a, double p):title(a),price(p){}
};
对,你没有看错,还有这种类似函数的方式给成员变量赋值。
这种写法本质就是为了简写成员变量的赋值操作,你同样可以用普通的方式定义构造函数:
book(char *a, double p){
title = a;
price = p;
}
在java语法中,你可以在初始化块中给成员变量中赋值,很容易理解。
但是在c++中提供这样一种以成员变量名作为函数名,参数列表是变量的值的形式初始化成员变量。
值得注意的是:参数初始化顺序与初始化表列出表量的顺序无关,参数初始化顺序只与成员变量在类中声明的顺序有关。
3.3 构造函数的参数默认值
PHP中的函数参数可以在调用之前就可以赋值一个默认值,因为PHP不支持函数的重载。通过这种方式,当你调用的函数时候没有给其他参数赋值,其他函数也会有一个默认值:
function exampleFunction($parm1,$parm2 = "world"){
echo $parm1 ." ". $parm2;
}
exampleFunction("hello")
//输出: hello world;
上面的代码可以很清楚的看到,第二个参数已经被赋值了默认值world
,当你没有给定第二个参数的时候,第二个参数仍然是有值的。
c++的构造函数也支持参数默认赋值,比如:
class book
{
public:
book(){}
book(char* a, double p = 5.0);
private:
double price;
char * title;
};
book::book(char* a, double p) //在定义函数的时候可以不指定默认参数
{
title = a;
price = p;
}
此时,当我们这样创建对象的时候:
Book Time("Time book")
调用的就是第二个构造函数,并且price
自动赋值为默认值5.0
但默认参数的构造函数会与某些构造函数重载冲突:
book(char* a);
如果我们再申明这样一个构造函数,之前创建对象就会发生编译错误,因为编译器不知道调用哪一个构造函数了。
换句话说,C++在支持构造函数的重载的条件下,基本上没必要使用带默认参数的构造函数。
* 3.4 转型、拷贝构造函数
3.4.1 转型构造函数
构造函数关于是否有参数可以分为:
- 不带参数构造函数
- 带参数构造函数(含带默认参数的构造函数)
其中带参数构造函数中有两种特殊的构造函数:
- 转型构造函数
- 拷贝构造函数
转型构造函数和普通的构造函数没啥区别,就是参数列表只有一个参数,创建对象的时候如果调用该构造函数,就是意味着将参数转换为了一个类的对象,这没什么特别的。
特别之处在于这里有一个隐式的类型转换:比如你创建一个字符串变量,但是你将该变量传入了一个参数为类对象的函数,这个时候就会自动的发生字符串到类对象的隐式对象类型转换:
class student
{
public:
student(char * n){name = n;}//转型构造函数
private :
char * name;
}
void fun(Student studentObj){};
char * name = “Harry Potter”;
fun(name);
这个时候字符串name自动转换为student类的对象,转换的过程中自动调用转型构造函数。
但隐式转换会带来调试的困难,可以在构造函数前加上explict
关键字,避免这种自动转换。
换句话说,转型构造函数就是参数只有一个的一类构造函数,能实现将参数转换为类对象的功能。
3.4.3 拷贝构造函数
4. 析构函数
在Java中无需自行设计析构函数,java有一套系统级的垃圾回收管理程序。
在创建对象的时候系统会自动调用构造函数,在对象需要被销毁的时候,系统也会自动调用析构函数。
与构造函数类似,析构函数也是一个成员函数,但是有如下特殊之处:
- 无返回值
- 没有参数,不能被重载,因此一个类也只能含有一个析构函数
- 函数名必须为“~类名”的形式,符号“~”与类名之间可以有空格
5. 类的高级用法
5.1 常量指针this
与java类似,在类的每个成员函数中包含一个常量指针,该指针指向调用该函数的对象。
与java类似,该指针只要是用来区分同名的成员变量与参数名称的:
void setprice(double price)
{
price = price;
}
5.2 new和delete操作符
c++创建对象有两种方式,一种就是普通的直接通过类名 对象名(构造函数参数列表)
创建,另一种就是通过new
关键字创建。
两种方式创建对象的空间分配方式有分别。
- 第一种空间分配在栈中,由系统可以直接管理,比如在代码块外,块内的对象就会被清理。
- 第二种的空间分配在堆上,大小可以很大, 但是销毁得由程序本身处理。
使用 new
关键字会自动调用构造函数
使用 delete
关键字会自动调用析构函数
5.3 const关键字
const表示一些不希望被人们修改的数据,与java语法中的final static
有类似之处,但是也有很多区别。
const关键字可以定义在成员变量、成员函、对象、对象的常引用上。
5.3.1 修饰(成员)变量
这个很好理解,在变量最前面加上关键字表示初始化之后禁止二次修改。const成员变量的初始化只能通过参数初始化表进行初始化
const int a=5;
5.3.2 修饰成员函数
const成员函数可以使用类中的所有成员变量,但是不能修改任何成员变量,也不能调用类中任何非const成员函数。
fun(形参) const{}
5.3.3 修饰类对象
用const修饰的类对象,该对象内的任何成员变量都不能被修改。
因此不能调用该对象的任何非const成员函数,因为对非const成员函数的调用会有修改成员变量的企图。
class A
{
public:
void funcA() {}
void funcB() const {}
};
int main
{
const A a;
a.funcB(); // 可以
a.funcA(); // 错误
const A* b = new A();
b->funcB(); // 可以
b->funcA(); // 错误
}
5.3.4 修饰类对象的引用
我们在有些函数参数中会使用到对象,这个时候我们不是直接将对象做为参数,而是使用对象的引用。
此时我们在对象的引用前加上关键字,避免函数对对象本身做出任何修改:
void display(const book &b)
{
b.setprice(59.9); //compile error
cout<<"The price of "<<b.gettitle()<<" is $"<<b.getprice()<<endl; //ok
}
5.4 static关键字
好了,这个关键字可以说是和java一致的了。
5.4.1 静态成员变量
该变量是同步于类的所有对象,也就是类的变量
5.4.2 静态成员函数
5.5 友元函数和友元类
5.5.1 友元函数
我们知道对象是无法直接调用私有变量或者私有函数的,只有在类中的函数才可以使用类的私有变量或者私有函数。
友元函数是可以直接访问类的私有成员的非成员函数。它是定义在类外的普通函数,它不属于任何类,但需要在类的定义中加以声明,声明时只需在友元的名称前加上关键字friend,其格式如下:
friend 返回类型 函数名(形式参数);
- 友元函数的声明可以放在类的私有部分,也可以放在公有部分,它们是没有区别的,都说明是该类的一个友元函数。
- 一个函数可以是多个类的友元函数,只需要在各个类中分别声明。
- 友元函数的调用与一般函数的调用方式和原理一致。
5.5.2 友元类
友元类的所有成员函数都是另一个类的友元函数,都可以访问另一个类中的隐藏信息(包括私有成员和保护成员)。
当希望一个类可以存取另一个类的私有成员时,可以将该类声明为另一类的友元类。定义友元类的语句格式如下:
friend class 类名;
当你在一个类中申明另一个类为该类的友元的时候,表明该友元类的所有成员函数都可以使用该类的所有信息。这种顺序是单向的。
其中:friend和class是关键字,类名必须是程序中的一个已定义过的类。
例如,以下语句说明类B是类A的友元类:
class A
{
…
public:
friend class B;
…
};
经过以上说明后,类B的所有成员函数都是类A的友元函数,能存取类A的私有成员和保护成员。
使用友元类时注意:
- 友元关系不能被继承。
- 友元关系是单向的,不具有交换性。若类B是类A的友元,类A不一定是类B的友元,要看在类中是否有相应的声明。
- 友元关系不具有传递性。若类B是类A的友元,类C是B的友元,类C不一定是类A的友元,同样要看类中是否有相应的申明。