纯虚函数
只能在基类中使用纯虚函数
基类本身也是能够定义纯虚函数的。 只是 声明了纯虚函数后 基类本身不能实例化
继承和动态内存分配:
基类有使用了new的话 必须显示定义析构函数 复制构造函数 赋值运算符
在基类使用了new获得内存空间而派生类没有的话 不需要再定义派生类的析构,复制构造,赋值运算符
如果派生类也用了new 就需要显式定义析构函数,复制构造函数,赋值运算符, 并且基类派生类也要在析构函数中各自delete 对应的new
派生类的复制构造函数
相当于*this = hs; 但不能还怎么写 这样写会形成递归调用 所以还是用函数表示法合适
当碰到派生类复写基类的友元函数涉及到基类的非公有成员变量的时候 可以将派生对象的引用或指针强转成基类的引用或指针
程序示例:
dma.h
#ifndef DMA_H_
#define DMA_H_
#include <iostream>
class baseDMA
{
private:
char * label;
int rating;
public:
baseDMA(const char * l = "null", int r = 0);
baseDMA(const baseDMA & rs);
virtual ~baseDMA();
baseDMA & operator=(const baseDMA & rs);
friend std::ostream & operator << (std::ostream & os, const baseDMA & rs);
};
class lacksDMA : public baseDMA
{
private:
enum {COL_LEN = 40};
char color[COL_LEN];
public:
lacksDMA(const char * s = "none", const char * l = "null", int r = 0);
lacksDMA(const char * c, const baseDMA & rs);
friend std::ostream & operator << (std::ostream & os, const lacksDMA & rs);
};
class hasDMA : public baseDMA
{
private:
char * style;
public:
hasDMA(const char* s = "none", const char * l = "null", int r = 0);
hasDMA(const char * s, const baseDMA & rs);
hasDMA(const hasDMA & hs);
~hasDMA();
hasDMA & operator=(const hasDMA & rs);
friend std::ostream & operator<<(std::ostream & os, const hasDMA & rs);
};
#endif
dma.cpp
#include "dma.h"
#include <cstring>
baseDMA::baseDMA(const char * l, int r)
{
label = new char [std::strlen(l) + 1];
std::strcpy(label, l);
rating = r;
}
baseDMA::baseDMA(const baseDMA & rs)
{
label = new char[std::strlen(rs.label) + 1];
std::strcpy(label, rs.label);
rating = rs.rating;
}
baseDMA::~baseDMA()
{
delete [] label;
}
baseDMA & baseDMA::operator=(const baseDMA & rs)
{
if (this == &rs)
return *this;
delete [] label;
label = new char[std::strlen(rs.label) + 1];
std::strcpy(label, rs.label);
rating = rs.rating;
return *this;
}
std::ostream & operator << (std::ostream & os, const baseDMA & rs)
{
os << "Label: " << rs.label << std::endl;
os << "Rating: " << rs.rating << std::endl;
return os;
}
lacksDMA::lacksDMA(const char * c, const char * l, int r) : baseDMA(l, r)
{
std::strncpy(color, c, 39);
color[39] = '\0';
}
lacksDMA::lacksDMA(const char * c, const baseDMA & rs) : baseDMA(rs)
{
std::strncpy(color, c, COL_LEN -1);
color[COL_LEN - 1] = '\0';
}
std::ostream & operator << (std::ostream & os, const lacksDMA & ls)
{
os << (const baseDMA &) ls;
os << "Color: " << ls.color << std::endl;
return os;
}
hasDMA::hasDMA(const char * s, const char * l, int r) : baseDMA(l, r)
{
style = new char[std::strlen(s) + 1];
std::strcpy(style, s);
}
hasDMA::hasDMA(const char * s, const baseDMA & rs) : baseDMA(rs)
{
style = new char[std::strlen(s) + 1];
std::strcpy(style, s);
}
hasDMA::hasDMA(const hasDMA & hs) : baseDMA(hs)
{
style = new char[std::strlen(hs.style) + 1];
std::strcpy(style, hs.style);
}
hasDMA::~hasDMA()
{
delete [] style;
}
hasDMA & hasDMA::operator=(const hasDMA & hs)
{
if (this == &hs)
return *this;
baseDMA::operator=(hs);
delete [] style;
style = new char[std::strlen(hs.style) + 1];
std::strcpy(style, hs.style);
return *this;
}
std::ostream & operator<< (std::ostream & os, const hasDMA & hs)
{
os << (const baseDMA &) hs;
os << "Style: " << hs.style << std::endl;
return os;
}
usedma.cpp
#include <iostream>
#include "dma.h"
int main()
{
using std::cout;
using std::endl;
baseDMA shirt("Portabelly", 8);
lacksDMA balloon("red", "Blimpo", 4);
hasDMA map("Mercator", "Buffalo Keys", 5);
cout << "Displaying baseDMA object:\n";
cout << shirt << endl;
cout << "Displaying lacksDMA object:\n";
cout << balloon << endl;
cout << "Displaying hasDMA object:\n";
cout << map << endl;
lacksDMA balloon2(balloon);
cout << "Result of lacksDMA copy:\n";
cout << balloon2 << endl;
hasDMA map2;
map2 = map;
cout << "Result of hasDMA assignment:\n";
cout << map2 << endl;
return 0;
}
纯虚函数示例:
acctabc.h
#ifndef ACCTABC_H_
#define ACCTABC_H_
#include <iostream>
#include <string>
class AcctABC
{
private:
std::string fullName;
long acctNum;
double balance;
protected:
struct Formatting
{
std::ios_base::fmtflags flag;
std::streamsize pr;
};
const std::string & FullName() const {return fullName;}
long AcctNum() const {return acctNum;}
Formatting SetFormat() const;
void Restore(Formatting & f) const;
public:
AcctABC(const std::string & s = "Nullbody", long an = -1,
double bal = 0.0);
void Deposit(double amt);
virtual void Withdraw(double amt) = 0;
double Balance() const {return balance;}
virtual void ViewAcct() const = 0;
virtual ~AcctABC(){}
};
class Brass : public AcctABC
{
public:
Brass(const std::string & s = "Nullbody", long an = -1,
double bal = 0.0) : AcctABC(s, an, bal) {}
virtual void Withdraw(double amt);
virtual void ViewAcct() const;
virtual ~Brass() {}
};
class BrassPlus : public AcctABC
{
private:
double maxLoan;
double rate;
double owesBank;
public:
BrassPlus(const std::string & s = "Nullbody", long an = -1,
double bal = 0.0, double ml = 500,
double r = 0.10);
BrassPlus(const Brass & ba, double ml = 500, double r = 0.1);
virtual void ViewAcct() const;
virtual void Withdraw(double amt);
void ResetMax(double m) {maxLoan = m;}
void ResetRate(double r) {rate = r;}
void ResetOwer() {owesBank = 0;}
};
#endif
acctABC.cpp
#include <iostream>
#include "acctabc.h"
using std::cout;
using std::ios_base;
using std::endl;
using std::string;
AcctABC::AcctABC(const string & s, long an, double bal)
{
fullName = s;
acctNum = an;
balance = bal;
}
void AcctABC::Deposit(double amt)
{
if (amt < 0)
cout << "Negative deposit not allowed; "
<< "deposit is cancelled.\n";
else
balance += amt;
}
void AcctABC::Withdraw(double amt)
{
balance -= amt;
}
AcctABC::Formatting AcctABC::SetFormat() const
{
Formatting f;
f.flag =
cout.setf(ios_base::fixed, ios_base::floatfield);
f.pr = cout.precision(2);
return f;
}
void AcctABC::Restore(Formatting & f) const
{
cout.setf(f.flag, ios_base::floatfield);
cout.precision(f.pr);
}
void Brass::Withdraw(double amt)
{
if (amt < 0)
cout << "Withdrawal amount must be positive; "
<< "withdrawal canceled.\n";
else if (amt <= Balance())
AcctABC::Withdraw(amt);
else
cout << "Withdrawal amount of $" << amt
<< " exceeds your balance.\n"
<< "Withdrawal canceled.\n";
}
void Brass::ViewAcct() const
{
Formatting f = SetFormat();
cout << "Brass Client: " << FullName() << endl;
cout << "Account Number: " << AcctNum() << endl;
cout << "Balance: $" << Balance() << endl;
Restore(f);
}
BrassPlus::BrassPlus(const string & s, long an, double bal,
double ml, double r) : AcctABC(s, an, bal)
{
maxLoan = ml;
owesBank = 0.0;
rate = r;
}
void BrassPlus::ViewAcct() const
{
Formatting f = SetFormat();
cout << "BrassPlus Client: " << FullName() << endl;
cout << "Account Number: " << AcctNum() << endl;
cout << "Balance: $" << Balance() << endl;
cout << "Maxmun loan: $" << maxLoan << endl;
cout << "Owed to bank: $" << owesBank << endl;
cout.precision(3);
cout << "Loan Rate: " << 100 * rate << "%\n";
Restore(f);
}
void BrassPlus::Withdraw(double amt)
{
Formatting f = SetFormat();
double bal = Balance();
if (amt <= bal)
AcctABC::Withdraw(amt);
else if (amt <= bal + maxLoan - owesBank)
{
double advance = amt - bal;
owesBank += advance * (1.0 + rate);
cout << "Bank advance: $" << advance << endl;
cout << "Finance charge: $" << advance * rate << endl;
Deposit(advance);
AcctABC::Withdraw(amt);
}
else
cout << "Credit limit exceeded. Transaction cancelled.\n";
Restore(f);
}
usebrass3.cpp
#include <iostream>
#include <string>
#include "acctabc.h"
const int CLIENTS = 4;
int main()
{
using std::cin;
using std::cout;
using std::endl;
AcctABC * p_clients[CLIENTS];
std::string temp;
long tempnum;
double tempbal;
char kind;
for (int i = 0; i < CLIENTS; i++)
{
cout << "Enter client's name: ";
getline(cin, temp);
cout << "Enter client's account number: ";
cin >> tempnum;
cout << "Enter opening balance: $";
cin >> tempbal;
cout << "Enter 1 for Brass Account or "
<< "2 for BrassPlus Account: ";
while (cin >> kind && (kind != '1' && kind != '2'))
cout << "Enter either 1 or 2: ";
if (kind == '1')
p_clients[i] = new Brass(temp, tempnum, tempbal);
else
{
double tmax, trate;
cout << "Enter the overdraft limit: $";
cin >> tmax;
cout << "Enter the interest rate "
<< "as a decimal fraction: ";
cin >> trate;
p_clients[i] = new BrassPlus(temp, tempnum, tempbal, tmax, trate);
}
while (cin.get() != '\n')
continue;
}
cout << endl;
for (int i = 0; i < CLIENTS; i++)
{
p_clients[i]->ViewAcct();
cout << endl;
}
for (int i = 0; i < CLIENTS; i++)
{
delete p_clients[i];
}
cout << "Done\n";
return 0;
}
类设计回顾:
默认构造函数:
构造函数:
构造函数不会被继承,且构造函数在完成工作前不会建立对象。
析构函数:
一定要定义显示析构函数来释放构造函数中的new分配的所有内存
正常情况下也应该显式提供一个析构函数
复制构造函数
赋值运算符
转换
可以禁止类型自动转换:
class Stra:
{
public:
explicit Star(const char *);
};
除非强制转换 否则 north = "polaris";报错
north = Stra("polaris"); // OK
转换函数:
要注意 除非真的有需要 否则尽量不吧 因为会跟运算符重载冲突产生二义性,例子:
按值传递对象还是按引用传递
一般都是按引用, 如果不修改对象的话 参数应该声明为const引用。
复制构造函数时俺值传递
虚函数时传递基类引用
尽量使用引用来做返回值(复杂类型)省时省力,但是如果是局部产生的这不要这样干 因为是局部变量 在函数用完之后就会释放掉。那时候引用或指针指向的地址是无效的。 这时候就需要按值返回。C++ 会自动调用赋值构造函数生成新的对象。
const:
形参加const 确保不会改变其形参
函数后面加const 确保不修改调用它的对象 也就是说对象本身没有哪个成员变量会被修改。
const在返回对象前面表示返回或指针指向的对象不能用于修改。
构造函数, 析构函数,赋值运算符不能继承
赋值运算符:
派生类对象赋值给基类对象 将调用基类的赋值运算符,然后只处理基类部分的形参,派生类的成员会被忽略
返回过来的话是无法运行的 因为不允许派生类引用指向基类对象
但是如果有只调用基类引用或者其他参数有默认值的构造函数则可以执行
或者有定义赋值运算符的形参类型是基类引用的
成员尽量放在私有
方法放在保护很有用。
如果派生类的友元函数能够使用基类的友元函数的话。可以将引用或指针强制转换成基类的引用或指针。然后使用转换后的指针或引用来调用基类的友元函数
第十三章完结 内容好多的说