1. 构造函数上接:
对象和类(一)
//类1.h #ifndef aa #define aa #include#include class Stock { private: std::string company;//公司名 long shares;//所持股票数量 double share_val;//每股价格 double total_val;//股票总价 void set_tot() { total_val=shares*share_val; } public: void acquire(const std::string &co,long n,double pr);//获得股票 void buy(long num,double price);//增持 void sell(long num,double price);//卖出股票 void update(double price);//更新股票价格 void show();//显示信息 }; #endif
1.1 声明和定义构造函数这是对象和类(一)中举得一个例子。我们发现我们无法像结构体那样Stock hot={"Cam",100,50.25};这样初始化对象,这是因为类的数据成员都是私有的,所以我们无法直接访问数据成员。当然了,如果Stock类的数据成员都是公有的,我们自然能像结构体那样直接初始化对象。
实际上,我们发现我们Stock::acquire()函数,就是扮演着初始化的对象的角色。
C++给类提供了一个构造函数,专门用来创造和初始化对象的。
Stock(const std::string &co,long n=0,double pr=0.0);//构造函数
上面这个就是构造函数的声明,这里使用了默认参数,我们发现构造函数的声明和成员函数的声明很像,但是注意,没有返回类型、构造函数的名称和类名称相同、构造函数是公有部分中的
Stock::Stock(const std::string &co,long n,double pr)
{
company=co;
if(n<0)
{
std::cout<<"数量不能是负数"
<
上面这个就是构造函数的定义,这段代码和acquire()的代码是一致的。
1.2 使用构造函数
C++提供了两种调用构造函数的方式。
- 显式调用
Stock food = Stock("World",250,1.25);
有没有发现啊?实际上这里利用了重载的技术,前面那个Stock是一个类,后面那个Stock是一个构造函数,而且我们发现构造函数的返回类型其实就是Stock类。 - 隐式调用
Stock food("World",250,1.25);
我们也可以使用动态内存分配的方式创建类对象
Stock *pfood =new Stock("World",250,1.25);
但是有一点请注意:
构造函数不同于一般的成员函数,它不能使用诸如food.Stock("World",250,1.25)的方式调用构造函数。
1.3 默认构造函数
默认构造函数和非默认构造函数的差别巨大,而且默认构造函数和非默认构造函数一样重要。
- 什么是默认构造函数?
上一节所说的构造函数都是非默认构造函数,非默认构造函数的功能时提供对象初始化。
默认构造函数的功能是,当对象未初始化时,给它进行默认初始化。
- 为什么要有默认构造函数?
C++禁止创建未初始化的对象。如果你只提供了非默认构造函数,那么Stock food;语句会直接报错,因为对象未初始化。但是如果你不提供非默认构造函数,那么Stock food;将不会报错,因为C++会提供隐式默认构造函数,自动给对象初始化。
- 有两种默认构造函数
隐式默认构造函数
如果没有提供任何构造函数,C++将自动提供隐式默认构造函数,它的定义可能是
Stock::Stock(){}
隐式默认构造函数不做任何操作,它只是告诉编译器,“你看,对象初始化过了!”
显式默认构造函数
我们可以自己定义一个默认构造函数,这个就叫做显式默认构造函数
一种简单的方法是,直接给非默认构造函数提供所有的默认参数,
Stock(const std::string &co="Error!",long n=0,double pr=0.0);
另一种方法是,利用构造函数的重载,显式默认构造函数原型:
Stock();可以发现默认构造函数不接受任何参数。
显式默认构造函数定义:
Stock::Stock(){company="no name";shares=0;share_val=0.0;total_val=0.0;}
当然了,为了重载解析成功,你只能二选一,两种方法仁者见仁,智者见智慧。
一般来说,类声明中,应该同时提供非默认构造函数和显式默认构造函数
这节所说的一切,本质上是函数重载,因为非默认构造函数和显式默认构造函数函数名相同,你调用哪个取决于语法。
Stock a;
Stock b("world",250,2.50);
上述代码中,对象a采用的是显式默认构造函数,对象b采用的是非默认构造函数。
2. 析构函数
析构函数完成清理工作,因此非常有用。
如果构造函数中使用new来创建数据,那么你必须写一个析构函数,用来delete各种数据项。当然了,如果你的构造函数中不使用动态内存分配,那么你完全不需要写析构函数。但是就算你不写析构函数,编译器也会自动给你生成一个什么也不干的隐式的默认析构函数。
-
析构函数什么时候被调用?
如果是静态对象,程序结束时,自动调用。
如果是自动对象,代码段结束时,自动调用。
如果是用new创建的对象,使用delete时自动调用。
总之,析构函数是自动调用的
-
析构函数的原型和实现
~Stock();可以看出来析构函数没有返回类型,而且不接受任何参数,析构函数的函数名是类名前加一个~
Stock::~Stock(){
cout<<"Bye!"<
3. 改进Stock类
//类2.h
#ifndef aa
#define aa
#include
#include
class Stock
{
private:
std::string company;//公司名
long shares;//所持股票数量
double share_val;//每股价格
double total_val;//股票总价
void set_tot()
{
total_val=shares*share_val;
}
public:
Stock();//默认构造函数
Stock(const std::string &co,long n=0,double pr=0.0);//构造函数
~Stock();//析构函数
void buy(long num,double price);//增持
void sell(long num,double price);//卖出股票
void update(double price);//更新股票价格
void show();//显示信息
};
#endif
//类2.cpp
#include
#include"类2.h"
Stock::Stock()
{
company="no name";
shares=0;
share_val=0.0;
total_val=0.0;
std::cout<<"注意!默认初始化!n";
}
Stock::Stock(const std::string &co,long n,double pr)
{ std::cout<<"注意!使用构造函数初始化,"<shares)
{
cout<<"卖出数量大于持有数量。交易失败。n";
}
else
{
shares-=num;
share_val=price;
set_tot();
}
}
void Stock::update(double price){
share_val=price;
set_tot();
}
void Stock::show()
{
using std::cout;
using std::endl;
using std::ios_base;
ios_base::fmtflags orig=cout.setf(ios_base::fixed,ios_base::floatfield);
std::streamsize prec =cout.precision(3);
cout<<"公司:"<
//类2main.cpp
#include
#include"类2.h"
int main()
{
{
using std::cout;
using std::endl;
cout<<"使用构造函数创建两个对象"<
PS D:studyc++path_to_c++> g++ -I .include -o 类2 .类2.cpp .类2main.cpp
PS D:studyc++path_to_c++> .类2.exe
使用构造函数创建两个对象
注意!使用构造函数初始化,公司1
公司:公司1 股票数量:12
每股价格:$20.000 总价值:$240.00
注意!使用构造函数初始化,公司2
公司:公司2 股票数量:2
每股价格:$2.000 总价值:$4.00
把stock1赋值给stock2
显示stock1和stock2
公司:公司1 股票数量:12
每股价格:$20.000 总价值:$240.00
公司:公司1 股票数量:12
每股价格:$20.000 总价值:$240.00
使用构造函数给对象重新初始化
注意!使用构造函数初始化,公司3
注意!再见,公司3
重新显示stock1
公司:公司3 股票数量:10
每股价格:$50.000 总价值:$500.00
完成!
注意!再见,公司1
注意!再见,公司3
代码段结束
可以试着跑一下上面的程序,可能结果有不同,因为不同编译器对Stock stock2=Stock("公司2",2,2.0);的处理不同,这句话可能创建临时对象。
stock1=Stock("公司3",10,50.0);这句是赋值语句是一定会创建临时对象的。
所以说,通常来说,初始化比赋值更高效。
//类3.cpp
#include"类2.h"
#include"类2.cpp"
int main(){
Stock a={"腾讯",100,45.0};
a.show();
Stock b{};
b.show();
const Stock c{"网易",200,25.00};
//c.show();
}
注意!使用构造函数初始化,腾讯
公司:腾讯 股票数量:100
每股价格:$45.000 总价值:$4500.00
注意!默认初始化!
公司:no name 股票数量:0
每股价格:$0.000 总价值:$0.00
注意!使用构造函数初始化,网易
注意!再见,网易
注意!再见,no name
注意!再见,腾讯
可以看出来,列表初始化语法同样适用于类。
但是我这边注释掉了一段代码//c.show();,这段代码运行会出错。
因为,c是一个const对象,c.show()无法确保c的数据成员不被修改,所以编译器会报错。
那么,我们想到的方法肯定是–const形参,但是,正如你所见,show()函数,没有形参,该怎么办?
const成员函数
C++允许我们这样声明、定义const成员函数:
void show() const;
void Stock::show() const
{
...
}
实际上,C++的初始化语句非常多,源于运算符的重载。
下面看一个神奇的例子。
如果一个类的构造函数是Bozo(int a);,它的构造函数只接受一个参数,那么初始化语句可以这样写Bozo x=6;。
接受一个参数的构造函数允许使用赋值语法将对象初始化:
classname object = value;
这种语法有危险!。
4. this指针
到目前为止,每个类成员函数只涉及一个对象,即调用它的对象。但是我们有时候需要涉及多个对象,在这种情况下,必须使用this指针。
例如,我们要求一个函数实现比较两个股票的总价值,并选出大的那个股票。
那么这个成员函数的原型肯定是:
const Stock & topval(const Stock &s)const;
首先这肯定是const成员函数,其次它的肯定要接收一个Stock对象,返回值也必须是一个Stock对象。
那么,调用这个成员函数时,
top=stock1.topval(stock2)和top=stock2.topval(stock1)是等价的。
下面我们来看看函数如何定义?
const Stock & Stock::topval(const Stock& s)const
{
if(s.total_val>total_val)
{
return s;
}
else
return ???;
}
有没有发现问题?函数无法返回调用这个函数的对象本身。C++为了解决这个问题,提供了this指针。
- this指针是什么?
这是一种特殊指针,它指向调用函数的对象,也就是说this指针的值是调用对象的地址。
每个成员函数都有一个this指针,*this就算调用对象。
如果是const成员函数,那么我们无法用this指针修改调用对象。
则上面的函数应该这样定义:
const Stock & Stock::topval(const Stock& s)const
{
if(s.total_val>total_val)
{
return s;
}
else
return *this;
}
这样一来,我们再次更新了我们的Stock类
5.对象数组
声明对象数组的方法和声明标准类型的数组的方法相同。
- Stock类的最终版本
//类4.h
#ifndef aa
#define aa
#include
#include
class Stock
{
private:
std::string company;//公司名
long shares;//所持股票数量
double share_val;//每股价格
double total_val;//股票总价
void set_tot()
{
total_val=shares*share_val;
}
public:
Stock();//默认构造函数
Stock(const std::string &co,long n=0,double pr=0.0);//构造函数
~Stock();//析构函数
void buy(long num,double price);//增持
void sell(long num,double price);//卖出股票
void update(double price);//更新股票价格
void show() const;//显示信息
const Stock & topval(const Stock & s)const;//返回价值更高的股票
};
#endif
//类4.cpp
#include
#include"类4.h"
Stock::Stock()
{
company="no name";
shares=0;
share_val=0.0;
total_val=0.0;
}
Stock::Stock(const std::string &co,long n,double pr)
{
company=co;
if(n<0)
{
std::cout<<"数量不能是负数"
<shares)
{
cout<<"卖出数量大于持有数量。交易失败。n";
}
else
{
shares-=num;
share_val=price;
set_tot();
}
}
void Stock::update(double price){
share_val=price;
set_tot();
}
void Stock::show() const
{
using std::cout;
using std::endl;
using std::ios_base;
ios_base::fmtflags orig=cout.setf(ios_base::fixed,ios_base::floatfield);
std::streamsize prec =cout.precision(3);
cout<<"公司:"<this->total_val)
{
return s;
}
else
return *this;
}
//类4main.cpp
#include"类4.h"
int main(){
Stock stocks[4]={
{"公司1",12,20.0},
{"公司2",200,2.0},
{"公司3",130,3.25},
{"公司4",60,6.5}
};
std::cout<<"展示对象数组:"<topval(stocks[i]);
}
std::cout<<"价值最高的股票是:"<show();
}
PS D:studyc++path_to_c++> g++ -I .include -o 类4 .类4.cpp .类4main.cpp
PS D:studyc++path_to_c++> .类4.exe
展示对象数组:
公司:公司1 股票数量:12
每股价格:$20.000 总价值:$240.00
公司:公司2 股票数量:200
每股价格:$2.000 总价值:$400.00
公司:公司3 股票数量:130
每股价格:$3.250 总价值:$422.50
公司:公司4 股票数量:60
每股价格:$6.500 总价值:$390.00
价值最高的股票是:
公司:公司3 股票数量:130
每股价格:$3.250 总价值:$422.50



