类介绍及其简单使用
c和c++最大的区别在哪里呢?c语言时面向过程的语言,c++是面向对象的编程。c++中也多了类的概念,类就是为了表示一类具有某些共同特性的对象。类中一般包括数据和类方法,类方法也就是要实现某些功能的函数,其实在c语言也可以实现类似功能,我们可以通过结构体的方式,类方法则可以通过函数指针来声明。但是结构体和类仍然有很多区别,类的功能更加丰富。
我们来看看下面的一个类,这个类是表示股票的一个类
class Stock
{
private:
char company[30];
int shares;
double share_val;
double total_val;
void set_tot(){ total_val = shares * share_val;}
public:
void acquire (const char *co, int n, double pr);
void buy(int num,double price);
void sell(int num,double price);
void update (double price);
void show();
}
class 关键字标识类的定义。Stock是这个类的类型名。这样我们就可以用Stock声明Stock类型的变量,例如
Stock xiaomi;
Stock hengda;
创建两个Stock对象,他们分别名为xiaomi和hengda。
接下来是要存储的数据,private表示接下来的数据和方法都是私有的,在类当中,默认的都是private,只能在类内部使用,若用public声明,则表示可以被外部类使用。
private和public描述了对类成员的访问控制,使用类对象的程序都可以直接访问共有部分,但只能通过公有成员函数来访问对象的私有成员。例如Stock类中的share成员,外部程序是不能直接访问的,但是可以通过公有的成员函数来访问,公有函数是外部程序与类中的私有数据的桥梁。
这样一个类的声明,我们通常放到头文件中,而类中方法的实现则放到c文件中,这和结构体类似。结构体的声明我们一般也是放到头文件中,结构体中定义的一些函数指针,则会在源文件中实现。结构体和类还有一点区别是,结构体中是无法声明私有数据的。
下面我们就实现类中定义的方法
定义类方法时,与定义普通的函数时有一个区别是,类方法的函数头使用作用域解析操作符(::)来指出函数所属的类。例如,Stock类的update方法的函数头如下:
void Stock::update(double price)
加::的作用是为了表明update函数是属于哪一个类的,因为又有可能有多个类中都有update方法,同样在类外部使用使用update方法时,也要用::指定是要使用哪一个类的update方法,当然,在Stock类内部时,可以直接使用update方法,不必加::
我们实现acuire方法
void Stock::acquire(const char *co,int n,double pr)
{
std::strncpy(company,co,29);
company[29] = '\0';
if (n < 0) {
std::cerr << "num of shares can't be negative."
<< company << "shares set to 0.\n";
shares = 0;
}
else
shares = n;
share_val = pr;
set_tot();
}
我们可以看到,类内部的私有数据company,shares等都是可以在方法中进行操作的。
如何使用这个方法呢,需要首先创建一个Stock类对象,例如 Stock xiaomi,然后xiaomi.acquire就可以使用这个方法了。
我们看一个简单的例子
#include <iostream>
#include <cstring>
class Stock
{
private:
char company[30];
int shares;
double share_val;
double total_val;
void set_tot(){ total_val = shares * share_val;}
public:
void acquire (const char *co, int n, double pr);
void buy(int num,double price);
void sell(int num,double price);
void update (double price);
void show();
};
void Stock::acquire(const char *co,int n,double pr)
{
std::strncpy(company,co,29);
company[29] = '\0';
if (n < 0) {
std::cerr << "num of shares can't be negative."
<< company << "shares set to 0.\n";
shares = 0;
}
else
shares = n;
share_val = pr;
set_tot();
}
void Stock::show()
{
using std::cout;
using std::endl;
cout << "Company: " << company
<< " Shares: " << shares << endl
<< " Share price : " << share_val
<< " Total Worth: " <<total_val <<endl;
}
int main()
{
Stock xiaomi;
Stock meituan;
xiaomi.acquire("xiaomi",100,20);
meituan.acquire("meituan",1500,100);
xiaomi.show();
meituan.show();
}
执行结果
所创建的每个新对象都有自己的存储空间,用于存储其内部变量和类成员;但同一个类的所有对象共享同一组类方法,也就是每种方法只有一个副本。
类的构造函数和析构函数
类的构造函数相当于类的初始化函数,因为我们之前说了,类中的私有数据只能通过公有函数来间接操作。我们无法像初始化一个int类型的值一样初始化类,例如
int a = 100;
构造函数的作用:对类进行初始化,通过设计合适的成员函数,将对象中的数据初始化。
构造函数需要向公有函数一样,在类中声明,然后定义构造函数。但是和公有函数有几点不同,
1、构造函数没有返回值。
2、构造函数的名称和类的名称保持一致,例如Stock类的构造函数的声明就形如Stock(); 定义形如Stock::Stock();
3、默认构造函数,默认构造函数没有参数,就是形如Stock();这样在定义一个对象的时候,就会调用默认的构造函数。而实际上,如果我们没有提供任何构造函数,就像上面的例子,c++将提供默认构造函数,但默认构造函数为空,形如
Stock::Stock()
{
}
4、构造函数的重载,可以创建多个同名的构造函数,只要每个函数的参数列表不同。例如可以在Stock类中声明这样的构造函数
Stock(const char * co,int n, double pr);
然后实现这个构造函数
Stock::Stock(const char *co,int n,double pr)
{
std::strncpy(company,co,29);
company[29] = '\0';
if (n < 0) {
std::cerr << "num of shares can't be negative."
<< company << "shares set to 0.\n";
shares = 0;
}
else
shares = n;
share_val = pr;
set_tot();
}
5、构造函数的使用
可以显示的调用,例如
Stock inter = Stock("inter",1000,15.5);
或隐式的调用
Stock inter ("inter",1000,15.5);
对于只是简单的创建一个类如 Stock inter,这就相当于Stock inter = Stock(),会调用默认的构造函数。
不能像使用其他公有方法那样使用构造函数,例如我们可以Stock::show()来调用Stock类的show方法,但是不能Stock::Stock()这样来调用构造方法,因为在创建对象之前,对象还是不存在的,因此构造函数被用来创建对象,而不能通过对象来调用。
析构函数则是构造函数的反方向,在对象过期时,程序自动调用一个特殊的成员函数,析构函数完成清理工作。例如在构造函数中我们有用new来分配内存,在析构函数中就应该用delete释放内存。析构函数同样没有返回值,并且没有参数,在Stock类中的析构函数的声明形如~Stock(),
我们来看一个构造函数使用的例子
头文件:
#include <iostream>
#include <cstring>
class Stock
{
private:
char company[30];
int shares;
double share_val;
double total_val;
void set_tot(){ total_val = shares * share_val;}
public:
Stock();
~Stock();
Stock(const char *co,int n,double pr);
void acquire (const char *co, int n, double pr);
void buy(int num,double price);
void sell(int num,double price);
void update (double price);
void show();
};
源文件:
#include "lei.h"
Stock::Stock()
{
std::cout << "using defualt constructor\n";
std:strcpy(company,"no name");
shares = 0;
share_val = 0;
total_val = 0;
}
Stock::~Stock()
{
std::cout << "bye bye!"<<std::endl;
}
Stock::Stock(const char *co,int n,double pr)
{
std::strncpy(company,co,29);
company[29] = '\0';
if (n < 0) {
std::cerr << "num of shares can't be negative."
<< company << "shares set to 0.\n";
shares = 0;
}
else
shares = n;
share_val = pr;
set_tot();
}
void Stock::acquire(const char *co,int n,double pr)
{
std::strncpy(company,co,29);
company[29] = '\0';
if (n < 0) {
std::cerr << "num of shares can't be negative."
<< company << "shares set to 0.\n";
shares = 0;
}
else
shares = n;
share_val = pr;
set_tot();
}
void Stock::show()
{
using std::cout;
using std::endl;
cout << "Company: " << company
<< " Shares: " << shares << endl
<< " Share price : " << share_val
<< " Total Worth: " <<total_val <<endl;
}
int main()
{
Stock xiaomi = Stock();
Stock meituan("meituan",1000,12);
xiaomi.show();
meituan.show();
}
执行结果:xiaomi对象执行了默认构造函数,meituan对象执行了我们重载的构造函数
this指针
在 C++ 中,每一个对象都能通过 this 指针来访问自己的地址。this 指针是所有成员函数的隐含参数。因此,在成员函数内部,它可以用来指向调用对象。常用的场景是在成员函数内部,将this作为参数传递出去。看一个简单的例子
#include <iostream>
using namespace std;
class Box{
public:
Box(){;}
~Box(){;}
Box* get_address() //得到this的地址
{
return this;
}
};
int main(){
Box box1;
Box box2;
// Box* 定义指针p接受对象box的get_address()成员函数的返回值,并打印
Box* p = box1.get_address();
cout << "box1 this addr: "<< p << endl;
cout << "box1 addr: "<< &box1 << endl;
p = box2.get_address();
cout << "box2 this addr: "<< p << endl;
cout << "box2 addr: "<< &box2 << endl;
return 0;
}
执行结果
从执行结果来看,返回this值正是当前对象的地址。this的作用正是基于此:对象内部获得对象的地址。
我们再换一个角度来看this问题,对象内部的成员函数内部是怎么知道当前对象也就是this的地址呢?实际上,当我们调用一个成员函数时,会用请求该函数的对象地址初始化 this。
例如,上面程序中的box1.get_address();实际上是
box1.get_address(&box1);