#include
using namespace std;
int main()
{
//cout中转换进制
int temp = 21;
cout <<"Hexadecimal:"<<hex<<temp<<endl;
cout <<"Octal:"<<oct<<temp<<endl;
cout <<"if don't change the environment:"<<temp<<endl;
cout <<"Decimal:"<<dec<<temp<<endl;
}
输出
Hexadecimal:15
Octal:25
if don't change the environment:25
Decimal:21
char类型是另外一种整型
#include
using namespace std;
int main()
{
//char
char c;
c=77;
cout <<"char c=77,when c is couting:"<<c<<endl;
c='M';
cout <<"char c=M,when c is couting:"<<c<<endl;
bool temp{'M'==77};
cout<<"'M'==77?:"<<temp<<endl;
cout<<"cout<<'$' = "<<'$'<<endl;
cout<<"another way to cout char:cout.put()--";
cout.put('$');
}
OUTPUT
char c=77,when c is couting:M
char c=M,when c is couting:M
'M'==77?:1
cout<<'$' = $
another way to cout char:cout.put()--$
#include
using namespace std;
int main()
{
//char
string s = "i love you";
for(int i=0;i<s.size();i++)
{
if(s[i]!=' ')
s[i] +='A'-'a';
}
cout << s<<endl;
}
16.3.1模板类vector(矢量)
#include
#include
#include
using namespace std;
int main()
{
//vec1.cpp,有初始化个数
const int NUM=5;
vector<int> rating(NUM);//只有初始化个数以后才能用get(cin,titile[i]),不然不能用[i]
vector<string> title(NUM);
for(int i=0;i<NUM;i++)
{
cout<<"Enter the title#"<<i+1<<": ";
getline(cin,title[i]);
cout<<"Enter the rating#"<<i+1<<": ";
cin>>rating[i];
cin.get();
}
for(int i=0;i<NUM;i++)
{
cout<<rating[i]<<":\t"<<title[i]<<endl;
}
}
output
Enter the title#1: KANKAN
Enter the rating#1: 2
Enter the title#2: ZHIDAO
Enter the rating#2: 3
Enter the title#3: NIS
Enter the rating#3: 1
Enter the title#4: ASD
Enter the rating#4: 5
Enter the title#5: DFS
Enter the rating#5: 2
2: KANKAN
3: ZHIDAO
1: NIS
5: ASD
2: DFS
#include
#include
#include
using namespace std;
template<typename T>//注意没有分号,并且在函数声明和定义之前都要使用
void showvec(vector<T> vec);
int main()
{
//迭代器
vector<double> d{1.2,3.4,5.6};
vector<double>::iterator it;//迭代器的声明,迭代器-->广义指针。
it = d.begin();
//auto it = d.begin()等价
cout<<"*it="<<*it<<endl;
cout<<"++it="<<*(++it)<<endl;
//打印vector
cout<<"----------------------"<<endl;
for(it=d.begin();it!=d.end();it++)
{
cout<<*it<<" ";//iterator using *p
}
cout << endl;
//输入
cout<<"----------------------"<<endl;
double temp;
while(cin>>temp)
{
d.push_back(temp);
if(getchar()=='\n')
{
break;
}
}
cout<<"after push_back: ";
showvec(d);
//erase()删除元素erase(iterator1,iterator2)-->删除【iterator1,iterator2)
cout<<"--------------------"<<endl;
d.erase(d.begin()+2);//删除单个元素
showvec(d);
d.erase(d.begin(),d.end()-3);//end()是超尾元素
showvec(d);
return 0;
}
template<typename T>
void showvec(vector<T> vec)
{
for(int i=0;i<vec.size();i++)
{
if(i==vec.size()-1)
cout << vec[i] << endl;
else
cout << vec[i]<<" ";
}
}
output
++it=3.4
----------------------
1.2 3.4 5.6
----------------------
7.7 8.8 9.9
after push_back: 1.2 3.4 5.6 7.7 8.8 9.9
--------------------
1.2 3.4 7.7 8.8 9.9
7.7 8.8 9.9
#include
#include
#include
#include //sort()
using namespace std;
bool worsethan(int e1,int e2);
void showvec(vector<int> vec);
int main()
{
//sort()
vector<int> scores;
int temp;
while(cin>>temp)
{
scores.push_back(temp);
if(getchar()=='\n')
{
break;
}
}
sort(scores.begin(),scores.end(),worsethan);
showvec(scores);
}
void showvec(vector<int> vec)
{
auto it=vec.begin();
for(;it<vec.end();it++)
{
if(it==vec.end()-1)
{
cout << *it << endl;
break;
}
cout << *it <<" ";
}
}
bool worsethan(int e1,int e2)
{
if(e1<e2)
return true;
else
return false;
}
4.2.3输入
cin使用空白(空格、制表符和换行符)来确定字符的结束位置
4.2.4作为一行输入
#include
#include
using namespace std;
int main()
{
const int arSize = 20;
string name1,check;
char name2[arSize],dessert[arSize];
cout <<"name1?"<<endl;
getline(cin,name1);//getline删除换行符,因此不需要cin.get(),getline(cin,string)适用于string
cout <<"name2?"<<endl;
cin.getline(name2,arSize);//只适用于cstring
cout<<"dessert?"<<endl;
cin.get(dessert,arSize);
cin.get();//cin.get(*,*)保留换行符到输入流中,需要用cin.get()去除
cout<<"Check?"<<endl;
cin>>check;//cin遇到空白停止,并且保留换行符
return 0;
}
输出
name1?
Wang Jing
name2?
Wang Jun Yi
dessert?
chocolate and cake
Check?
check
4.2.5数字和字符串混合输入
#include
#include
using namespace std;
int main()
{
const int arSize = 20;
int i1,i2;
char cstr1[arSize],cstr2[arSize];
cout<<"cstr1?"<<endl;
cin.getline(cstr1,arSize);
cout<<"cstr2?"<<endl;
cin>>cstr2;//若有空格,读到空格就停止
cin.get();//吞掉cstr2带来的空格
cout<<"i1?"<<endl;
cin>>i1;
cout<<"i2?"<<endl;
cin>>i2;//输入整数时无需考虑换行符的问题
return 0;
}
输出
cstr1?
I love sea.
cstr2?
blablabla
i1?
1
i2?
2
tips:
cout<<R"+*("who wouldn't it?", she whispered.)+*";
4.7.6使用new来创建动态数组
#include
using namespace std;
int main()
{
int* pi = new int;
delete pi;
int* psome = new int[10];
delete [] psome;//free the dynamic array
*psome = 0;//first element
psome[1] = 1;//second element,using pointer as an array
psome[2] = 2;
cout << "psome[1] = " << psome[1] << endl;
cout <<"psome[2] = " << psome[2] << ", *(psome+2) = "<< *(psome+2);
}
输出
psome[1] = 1
psome[2] = 2, *(psome+2) = 2
4.8.3指针和字符串
#include
#include
using namespace std;
int main()
{
char animal[20] = "bear";
const char* bird = "wren";//char* = char [];bird holds the address
char* ps;//只能让ps指向特定地址,ps doesn't point to allocated place
cout << animal << " and " << bird << endl;
cout <<"Enter a name of one animal:"<< endl;
cin >> animal;
ps = animal;
cout <<"Before using strcpy():\n";
cout << "animal = " << animal << ", at "<< (int *) animal << endl;
cout <<"ps = " << ps << ", at "<< (int *) ps <<endl;
ps = new char[strlen(animal)+1];
strcpy(ps,animal);
cout <<"After using strcpy():\n";
cout << "animal = " << animal << ", at "<< (int *) animal << endl;
cout <<"ps = " << ps << ", at "<< (int *) ps <<endl;
delete [] ps;
return 0;
}
输出
bear and wren
Enter a name of one animal:
fox
Before using strcpy():
animal = fox, at 0x61fdf0
ps = fox, at 0x61fdf0
After using strcpy():
animal = fox, at 0x61fdf0
ps = fox, at 0x6a6eb0
4.8.4指针和结构
#include
#include
using namespace std;
struct sweetheart
{
string name;
float height;
double weight;
void show()
{
cout << name<<": "<<endl;
cout <<"~~~~~~~~~~~~~~~~~~~~~~\n";
cout <<"\t"<<height<<"m\n\t"<<weight<<"kg.";
}
};
int main()
{
sweetheart* pt = new sweetheart;
cout << "What's your little one's name?\n";
getline(cin,pt->name);//指针结构,指针->成员,(*指针).成员
cout <<"Height?\n";
cin >> (*pt).height;
cout << "Weight?\n";
cin >> (*pt).weight;
pt->show();
delete pt;
}
OUTPUT
What's your little one's name?
WJY
Height?
1.75
Weight?
72
WJY:
~~~~~~~~~~~~~~~~~~~~~~
1.75m
72kg.
逗号运算符,
#include
#include
using namespace std;
int main()
{
string temp="WangJingYi";
for(int b=0,e=temp.size()-1;b<e;b++,e--)
{
char a=temp[b];
temp[b]=temp[e];
temp[e]=a;
}
cout << temp << endl;
int i,j,result;
cout << "i=1,j=4*i = " << (i=1,j=4*i) << endl;//逗号运算符先计算第一个,返回第二个的值
result = 17,240;//逗号运算符的优先级最低
cout << result << endl;
result = (17,240);
cout << result << endl;
}
字符串的比较
#include
#include
#include
using namespace std;
int main()
{
//ctring
for(char temp='a';temp<='z';temp++)
cout << temp;
char cstr1[10]="Zoo", cstr2[20]="activity";//不能直接==,数组名是指针
cout <<endl<<strcmp(cstr1,cstr2)<<endl;//0 for equal,1 for >,-1for /大写<小写
char name[10]="?angJunYi";
for(char ch='T';strcmp(name,"WangJunYi");ch++)
{
cout << name << endl;
name[0]=ch;
}
//string
cout << ("WangJunYi"=="SoulMate") << endl;
}
int main()
{
vector<int> temp;
int a;
while(cin>>a)
{
temp.push_back(a);
if(getchar()=='\n')
break;
}
}
#include
using namespace std;
int main()
{
char ch;
cin.get(ch);
int count=0;
while(cin.fail()==false)//test for eof
{
cout<<ch<<endl;
count++;
cin.get(ch);
}
}
#include
#include
using namespace std;
const int Max=5;
int main()
{
cout<<"please input your golf scores:"<<endl;
int scores[Max];
for(int i=0;i<Max;i++)
{
cout<<"Scores#"<<i+1<<": ";
while(!(cin>>scores[i]))
{
cin.clear();//重置cin
while(cin.get()!='\n')//清楚错误输入
{
continue;
}
cout<<"please enter a number:";
}
}
}
输出
please input your golf scores:
Scores#1: fuck you
please enter a number:99
Scores#2: fuck me
please enter a number:100
Scores#3: 200
Scores#4: 300
Scores#5: 400
arr[i]=*(arr+i)
&arr[i]=arr+i
#include
using namespace std;
int sumArr(const int* begin,const int* end);
int main()
{
//函数与一维数组
int Arr[]{19,24,12,78,34};
int sum1=sumArr(Arr+2,Arr+5);
cout <<"sumArr(Arr+2,Arr+5) = "<< sum1 << endl;
}
int sumArr(const int* begin,const int* end)
{
const int* ptr=begin;
int sum=0;
for(;ptr<end;ptr++)
{
sum+=*ptr;
}
return sum;
}
#include
using namespace std;
int sumArr(int arr[][4],int size);
int main()
{
int arr[][4]=
{
{4,5,2,6},
{78,45,13,80},
{67,42,59,83}
};
int sum2=sumArr(arr,2);
cout <<"sumArr(arr,2) = "<<sum2<<endl;
}
int sumArr(int (*arr)[4],int size)//注意二维数组的两种写法 arr[][4]==(*arr)[4]
{
int sum=0;
for(int i=0;i<size;i++)
{
for(int c=0;c<4;c++)
{
sum+=arr[i][c];//arr作为指针名和数组名,可以在函数内看作二维数组名使用
}
}
return sum;
}
程序的分解:
- 头文件:包括结构声明、使用这些结构的函数的声明
- 源代码文件:包含与结构有关的函数的代码
- 源代码文件:包含调用与结构相关的函数的代码
//头文件通常包括:函数原型,#define或#const定义的符号常量、结构声明、类声明、模板声明、内联函数
#ifndef TEST_H_
#define TEST_H_
//通常用#define定义符号常量,如#define MAXIMUM 4096,此处如果将#define用于名称,足以完成该名称的定义。
struct Test
{
/* data */
};
class TEST
{
private:
/* data */
public:
Test(/* args */);
~Test();
void show();
};
#endif
#include
#include"test.h" //双引号--用户自定义头文件
using namespace std;
Test::Test()
{
}
Test::~Test()
{
}
void Test::show()
{
}
#include
#include"test.h" //双引号--用户自定义头文件
using namespace std;
int main()
{
}
#include
#include
using namespace std;
//默认参数
//静态变量
int cats = 22;//外部链接性;
int elephant;//外部链接性
static int dogs = 11;//内部链接性
extern int cows = 100;//外部链接性
int main()
{
static int tigers = 2;//无链接性;
int cats = 0;
cout<<"Local cats: "<<cats<<endl;
cout<<"global cats: "<< (::cats) <<endl;//域解析符
}
#include
#include
using namespace std;
//默认参数
//静态变量
int cats = 22;//外部链接性;
int elephant;//外部链接性
static int dogs = 11;//内部链接性
extern int cows = 100;
#include //不需要引入文件,需要编译
using namespace std;
extern int cows;//引用声明,没有初始化
int main()
{
cout << cows << endl;
}
PS D:\mycpp\learning> g++ staticPyototype.cpp staticUsing.cpp
staticPyototype.cpp:10:12: warning: 'cows' initialized and declared
extern'
extern int cows = 100;
^~~~
PS D:\mycpp\learning> ./a.exe
100
面向对象编程OOP特性:
—抽象
—封装和数据隐藏
—多态
—继承
—代码的可重用性
OOP优点:易维护、易复用、易扩展,可以设计出低耦合的系统,使系统更加灵活和易于维护
用户接口:
用户与数据交互的方式有三种:初始化、更新和报告
C++中的抽象:实现抽象接口的类设计
类是一种将抽象转换为用户自定义类型的C++工具,将数据表示和操纵数据的方法组合在一起。类首字母大写
访问控制关键字:protected、public、private
封装:将实现细节与抽象分开成为封装,例如数据隐藏(防止程序直接访问数据)、将类函数定义与类声明放在不同的文件
”::“:作用域解析运算符
C/S模型:客户/服务器模型
客户是使用类的程序,类声明和类方法构成了服务器。
—客户通过以公有方式定义的接口使用服务器
—服务器的设计者可以修改类设计的实现细节,而不能修改接口。
为避免成员名和参数名重复,①:private: m_company ②: private:company_
destructor: 类对象生命周期结束时,程序自动调用析构函数完成清理。
调用时:
静态存储类对象:程序结束时
自动存储类对象:代码块执行完成时
new对象:delete对象
//stock.h -- Stock class interface接口
#ifndef STOCK_H_
#define STOCK_H_
#include
#include
using namespace std;
class Stock
{
private://可访问:公有成员函数、友元
std::string company;
long shares;
public:
Stock();//default constructor
Stock(const std::string &co,long n=0, double pr = 0.0);
~Stock();//destructor
}; //note semicolon at the end
#endif
#include
#include"stock.h" //注意双引号
using namespace std;
Stock::Stock(){};
Stock::Stock(const std::string &co,long n=0, double pr = 0.0)
{
this->company=co;
this->shares=n;
}
Stock::~Stock()
{
cout<<"You can delete the new"<<endl;
}
int main()
{
Stock stock1{"wangjing",2,1.1};
Stock stock2;
stock2 = stock1;//对象赋值给对象
stock1 = Stock("wangjunyi",100,3.0);//将新值赋给stock1
return 0;
}
//stock.h -- Stock class interface接口
#include
#include
using namespace std;
class Stock
{
private://可访问:公有成员函数、友元
std::string company;
long shares;
public:
Stock();//default constructor
Stock(const std::string &co,long n=0, double pr = 0.0);
~Stock();//destructor
void show() const;//不改变调用对象
}; //note semicolon at the end
Stock::Stock(){};
Stock::Stock(const std::string &co,long n=0, double pr = 0.0)
{
this->company=co;
this->shares=n;
}
Stock::~Stock()
{
cout<<"You can delete the new"<<endl;
}
void Stock::show() const//promises not to change the invoking object
{
cout<<this->company;
}
int main()
{
//const 成员函数
const Stock s1{"wangjing",1,1.2};
s1.show();//good
}
this指针用来指向调用成员函数的对象
用于成员函数中涉及多个对象的操作
#include
#include
using namespace std;
class Stock
{
private:
std::string company;
long shares;
public:
Stock();//default constructor
Stock(const std::string &co,long n=0, double pr = 0.0);
~Stock();//destructor
Stock& topShares(Stock& s);//隐式访问stock1,显式访问s
}; //note semicolon at the end
Stock::Stock(){};
Stock::Stock(const std::string &co,long n=0, double pr = 0.0)
{
this->company=co;
this->shares=n;
}
Stock::~Stock(){};
Stock& Stock::topShares(Stock& s)
{
if(s.shares>shares)
return s;
else
return *this;//注意this是地址
}
要创建类对象数组,必须有default constructor
int main()
{
Stock myStock[4];//声明对象数组-->default constructor
Stock stocks[2] = {
Stock("one",1,1.1),
Stock("two",2,2.2)
};//explicit destructor
}
#include
#include
using namespace std;
class Stock
{
private:
std::string company;
long shares;
public:
Stock();
Stock(const std::string &co,long n=0, double pr = 0.0);
const Stock& topShares(Stock& s) const;
};
Stock::Stock(){};
Stock::Stock(const std::string &co,long n=0, double pr = 0.0)
{
this->company=co;
this->shares=n;
}
Stock::~Stock(){};
const Stock& Stock::topShares(Stock& s) const
{
if(this->shares>s.shares)
return *this;
else
return s;
}
int main()
{
const int number=4;
Stock stocks[number]={
Stock("one",4,12),
Stock("two",23,12.2),
Stock("three",12,22),
Stock("four",2,75)
};
const Stock* top=&stocks[0];
for(int i=1;i<number;i++)
{
top=&top->topShares(stocks[i]);
}
}
类作用域:在类中定义的成员变量与成员函数的作用域都为整个类,只在类中已知。
错误:
#include
using namespace std;
class Bakery
{
private:
const int Months=12;
double costs[Month];//声明类只是描述了对象的形式,并没有创建对象。因此在创建对象之前,没有用于储存值的空间
}
正确①:
class Bakery
{
private:
enum{MONTHS=12};//枚举,不会创建类数据变量。
double costs[MONTHS];
}
正确②:static关键词
class Bakery
{
private:
static const int MONTHS=12;//静态变量与其他变量储存在一起,不储存在对象中
double costs[MONTHS];
}
static详解见十二章
int main()
{
enum number_old{one,two,three,four};//unscoped
enum class number{one,two,three,four};//scoped
enum class Tshirt{small,medium,large};//scoped
//限定符
number One=number::one;
number_old One_old=one;
//作用域内枚举不能隐式地转换为整型
if(One_old>1)
{
cout<<"implicit type conversion."<<endl;
}
int k=One;//wrong
int k=int(One);//强制类型转换
}
抽象数据类型(abstract data type)ADT
#include
using namespace std;
int main()
{
//接受输入
char ch;
while(cin>>ch && toupper(ch)!='Q')
{
if(cin.get()!='\n')
continue;
if(!isalpha(ch))
continue;
switch(ch)
{
case 'A':
case 'a':
cout<<"Right a!"<<endl;
}
}
}
运算符重载是一种形式的C++多态
**函数重载/函数多态:**定义多个名称相同但特征标(参数列表)不同的函数
运算符函数格式
operator op()
operator*()//重载*运算符
district1 = sid + sara;//等价于
district1 = sid.operator+(sara);//implicit invoking sid, explicit invoking sara
#include
using namespace std;
class Time
{
private:
int hour;
int minute;
public:
Time();
Time(int h,int m=0);
void addHour(int h);
void addMin(int m);
void reset(int h=0,int m=0);
Time operator+(Time& t) const;
friend ostream& operator<<(ostream& os,Time& t) ;//不能加const
};
Time::Time()
{
hour=0;
minute=0;
}
Time::Time(int h,int m)//默认值参数只能在声明里,定义时不写
{
hour=h;
minute=m;
}
void Time::addHour(int h)
{
hour+=h;
}
void Time::addMin(int m)
{
minute+=m;
hour+=minute/60;
minute%=60;
}
void Time::reset(int h,int m)
{
hour=h;
minute=m;
}
Time Time::operator+(Time& t) const
{
Time sum;//to return
sum.minute=minute+t.minute;
sum.hour=hour+t.hour+sum.minute/60;
sum.minute%=60;
return sum;
}
ostream& operator<<(ostream& os,Time& t) //定义友元函数时没有前面的friend
{
os<<"Time: "<<t.hour<<" h, "<<t.minute<<" m.";
return os;
}
int main()
{
Time t1=Time(1,55);
Time t2=Time(2,35);
Time t3=Time(1,20);
Time t4 = t1+t2+t3;
cout<<t4<<endl;//cout<<(t1+t2+t3)<invalid:临时变量
}
OUTPUT
Time: 5 h, 50 m.
重载限制:
- 重载后的运算符至少有一个是用户定义的类型,防止用户为标准类型重载运算符
- 使用运算符时不能违反原来的句法规则。例如不能将%用作%x
不能修改运算符优先级
不能创建新运算符
不能重载以下运算符:
sizeof
.
::
?:
强制类型转换运算符
友元:
友元函数
*友元类
*友元成员函数
是
友元函数
特殊的非成员函数,可以访问类的私有成员
解决t2=t13.75 valid,而t2=3.75t1 invalid的问题
//operator declaration
Time operator*(double m);//t2=t1*2.75
friend Time operator*(double m,Time& t);//goes in class declaration//t2=2.75*t1
//operator definition
Time operator*(double m,Time& t){};//friend not used in definition
friend ostream& operator<<(ostream& os,Time& t);//按引用传递使用的内存和时间都比按值传递少
ostream& operator<<(ostream& os,Time& t)
{
os<<t.hour<<" "<<t.minute;
return os;
}
❗❗❗
只含有一个参数的构造函数可作为转换函数
#include
#include
using namespace std;
class Stonewt
{
private:
double stone;
double pound;
public:
Stonewt()
{
stone=0;
pound=0;
}
Stonewt(double p)//隐式类型转换,只有接受一个参数的构造函数才可被作为转换函数
{
pound=p;
stone=1.08*p;
}
friend ostream& operator<<(ostream& os,Stonewt s)
{
os<<"I'm "<< s.pound <<" pound and "<<s.stone<<" stones.";
return os;
}
};
int main()
{
Stonewt s1;
s1=1900.0;//隐式类型转换
cout<<s1<<endl;
}
OUTPUT
I’m 1900 pound and 2052 stones.
explicit 用于关闭自动特性。
#include
#include
using namespace std;
class Stonewt
{
private:
double stone;
double pound;
public:
Stonewt()
{
stone=0;
pound=0;
}
explicit Stonewt(double p)
{
pound=p;
stone=1.08*p;
}
friend ostream& operator<<(ostream& os,Stonewt s)
{
os<<"I'm "<< s.pound <<" pound and "<<s.stone<<" stones.";
return os;
}
};
int main()
{
Stonewt s1;
s1=Stonewt(1900.9);//valid
//s1=1900.9 -- invalid
cout<<s1<<endl;
}
OUTPUT
I'm 1900.9 pound and 2052.97 stones.
将stonewt->double:转换函数
转换函数:
形式:operator typeName()
注意:
- 转换函数必须是类方法
- 转换函数不能指定返回类型
- 转换函数不能有参数
#include
#include
using namespace std;
class Stonewt
{
private:
double stone;
double pound;
public:
Stonewt()
{
stone=0;
pound=0;
}
explicit Stonewt(double p)//explicti double-to-Stonewt conversion
{
pound=p;
stone=1.08*p;
}
operator double()//conversion function for this class to double
{
return pound;
}
};
int main()
{
Stonewt s1;
s1=Stonewt(1900.9);//valid
double d1=double(s1);
cout<<"d1 = "<<d1<<endl;;
}
加法总结
完成s1=1.75+s2和s1=s2+1.75
//运算符重载函数+友元函数-->速度快,对程序员要求高
Stonewt operator+(double x);
friend Stonwt operator(double x,Stonewt& s)
//友元+Stoneet(double d)构造函数-->定义函数少,出错概率小,系统开销高(内存和速度),依赖于构造函数Stonewt(double)
friend Stonewt(const Stonewt& s1,const Stonewt& s2)
在程序运行时,而不是编译时,确定诸如使用多少内存的问题。
new作用:
StringBad* ptr=new StringBad(mottoo);
ptr=new出来的空间
*ptr=mottoo
如果没有定义,C++自动提供以下函数:
- 默认构造函数
- 默认析构函数
- 复制构造函数
- 赋值运算符
- 地址运算符
StringBad类中的问题是由隐式复制构造函数和隐式赋值运算符引起的
复制构造函数:
按引用传递
默认的复制构造函数:浅复制,逐个复制非静态成员,复制的是成员的值。
//深度拷贝
String::String(const String& st);
{
num_strings++;
len=st.len;
str=new char[len+1];
std::strcpy(str,st.str);
}
赋值运算符:
浅拷贝。导致同一位置调用两次析构函数
赋值运算符是只能由类成员函数重载的运算符之一
//赋值运算符深度拷贝
String & String::operator=(const String & st)
{
if(this==&st)//检查自我复制
{
return *this;
}
delete [] str;//free old string
len=st.len;
str=new char[len+1];
std::strcpy(str,st.str);
}
进一步重载赋值运算符:
StringBad sb1=“C++”;//需要调用StringBad(const char*)和StringBad(const StringBad& st)
为了提高处理效率,最简单的方法的重载赋值运算符,使之能够直接使用常规字符串,这样就不用创建和删除临时变量了
char& String::operator[](int i)//重载中括号访问法
{
return str[i];
}
// 返回char&,便于:
// String a{"wangjing"};
// a[0]='f';
静态成员函数后果:
- 不能通过对象调用静态成员函数。静态成员函数不能用this指针。调用方法:int count = String::HowMany();
- 由于静态成员函数不与特定的对象相关联,因此只能使用静态数据成员。HowMany()可以访问静态成员num_string,但不能访问str和len.
函数声明:static int HowMany();
函数定义:int String::HowMany(){return num_strings;}//如果独立
//static function
static int HowMany(){return num_strings};
#ifndef STRING_H_
#define STRING_H_
#include
using std::ostream;
using std::istream;
class String
{
private:
char* str;//类成员str是一个指针,因此构造函数必须new提供内存来储存字符串
int len;
static int num_strings;//不能在声明时初始化静态成员变量
static const int CINLIM = 80;
public:
//constructors and other methods
String();
String(const char* s);
String(const String &);//copy constructor//显式复制构造函数
~String();
int length() const {return len;}
//overload operator methods
String & operator=(const String &);
String & operator=(const char*);
char& operator[](int i);
const char& operator[](int i) const;
//overload operator friends
friend bool operator<(const String& st1,const String& st2);
friend bool operator>(const String& st1,const String& st2);
friend bool operator==(const String& st1,const String& st2);
friend ostream & operator<<(ostream & os,String& st);
friend istream & operator>>(istream & os,String& st);
//static function
static int HowMany();
};
#endif
#include
#include"string.h"
using std::cin;
using std::cout;
int String::num_strings=0;//initialize the static class meber
//constructors and other methods
String::String()
{
len=0;
str=new char[1];//与delete[] str对应
str[0]='\0';//不能是str='\0',str是数组名/地址
num_strings++;
}
String::String(const char* s)
{
}
String::String(const String & st)//copy constructor//显式复制构造函数
{
num_strings++;
len=st.len;
str=new char[len+1];
std::strcpy(str,st.str);
}
String::~String()
{
num_strings--;
}
//overload operator methods
String & String::operator=(const String & st)
{
if(this==&st)//检查自我复制
{
return *this;
}
delete [] str;//free old string
len=st.len;
str=new char[len+1];
std::strcpy(str,st.str);
}
String & String::operator=(const char* s)
{
delete[] str;
len=std::strlen(s);
str=new char[len+1];
std::strcpy(str,s);
return *this;
}
char& String::operator[](int i){return str[i];}//重载中括号访问法
const char& String::operator[](int i) const{return str[i];};
//overload operator friends
bool operator<(const String& st1,const String& st2)//声明外不需要有friend和String::
{
return(std::strcmp(st1.str,st2.str)<0);
}
bool operator>(const String& st1,const String& st2)
{
return(strcmp(st1.str,st2.str)>0);
}
bool operator==(const String& st1,const String& st2)
{
return(strcmp(st1.str,st2.str)==0)
}
ostream & operator<<(ostream & os,String& st)
{
os<<st.str;
return os;
}
istream & operator>>(istream & is,String& st)
{
char temp[String::CINLIM];//如何调用静态变量
is.get(temp,String::CINLIM);
if(is)
st=temp;
while(is&&is.get()!='\n')
continue;
return is;
}
//static function
int String::HowMany(){return num_strings;}//如果函数定义是独立的,不能包含关键字static
三法则(Rule of Three):
在构造函数中使用new时
- 显式复制构造函数
- (显式重载构造函数)
- 显式重载赋值运算符
- 显式析构函数
new与delete对应,new[]与delete[]对应
使用const引用的常见原因是旨在提高效率
- 返回对象将调用复制构造函数,而返回引用不会。
- 引用指向的对象应在调用函数之前就存在。
- const String& Max(const String& st1,const String& st2);//const与const对应
原始类:基类
继承类:派生类(derive)
#include
#include
using namespace std;
//基类
class TableTennisPlayer
{
private:
string firstname;
string lastname;
bool hasTable;
public:
TableTennisPlayer(const string & fn="none",const string &ln="none",bool ht=false);
friend ostream & operator << (ostream& os,TableTennisPlayer & player)//重载<<运算符
};
TableTennisPlayer::TableTennisPlayer(const string & fn,const string &ln,bool ht):
firstname(fn),lastname{ln},hasTable(ht) {}//成员初始化列表,可以为const赋值
ostream & operator << (ostream& os,TableTennisPlayer & player)
{
os<<player.fi rstname<<" "<<player.lastname<<endl;
return os;
}
派生类特点:
派生类对象存储了积累的数据成员(派生类继承了基类的实现)–>private is private
派生类对象可以使用积累的方法(派生类继承了基类的接口)
派生类继承特性中添加:
派生类需要自己的构造函数
派生类可以根据需要添加额外的数据成员和成员函数
派生类构造函数要点:
· 创建派生类对象时,程序首先调用基类构造构造函数,然后再调用派生类构造函数。基类构造函数负责初始化继承的数据成员,派生类构造函数主要用于初始化新增的数据成员。派生类构造函数总是调用一个基类构造函数。可以使用初始化器列表语法指明要使用的基类构造函数,否则将使用默认的基类构造函数。
· 派生类对象过期时,程序先调用派生类析构函数,在调用基类析构函数。
#include
#include
using namespace std;
//基类
class TableTennisPlayer
{
private:
string firstname;
string lastname;
bool hasTable;
public:
TableTennisPlayer(const string & fn="none",const string &ln="none",bool ht=false);
friend ostream & operator << (ostream& os,TableTennisPlayer & player)
};
TableTennisPlayer::TableTennisPlayer(const string & fn,const string &ln,bool ht):
firstname(fn),lastname{ln},hasTable(ht) {}//成员初始化列表,可以为const赋值
ostream & operator << (ostream& os,TableTennisPlayer & player)
{
os<<player.firstname<<" "<<player.lastname<<endl;
return os;
}
//派生类
class RatedPlayer: public TableTennisPlayer
{
private:
unsigned int rating;//add a data member
public:
RatedPlayer(unsigned int r=0,const string & fn ="none",const string & ln="none",
bool ht =false): TableTennisPlayer(fn,ln,ht)
{
rating=r;
}
RatedPlayer(TableTennisPlayer TTP,unsigned int r=0):TableTennisPlayer(TTP),rating(r) {};
};
基类和派生类关系:
- 派生类可以使用基类的方法,条件是方法不是私有的。
- 基类指针可以在不显式类型转换的情况下指向派生类对象。
- 基类引用可以在不显式转换的情况下引用派生类对象。
指针/引用:
- 基类指针或引用只能用于调用基类方法
- 不可以将基类对象和地址赋给派生类引用和指针
- 结果:基类引用定义的函数或指针参数可用于基类对象和派生类对象。
#include
#include
using namespace std;
class TableTennisPlayer
{
private:
string name;
bool hasTable=false;
public:
TableTennisPlayer()
{
name="none";
}
TableTennisPlayer(string n,bool ht)
{
name=n;
hasTable=ht;
}
void show() const
{
cout<<"I'm "<<name<<" with "<< hasTable <<" table."<<endl;
}
};
class RatedPlayer:public TableTennisPlayer
{
private:
int rating;
public:
RatedPlayer():TableTennisPlayer(),rating(0){};
RatedPlayer(string n,bool ht,int r):TableTennisPlayer(n,ht),rating(r){};
RatedPlayer(int r,const TableTennisPlayer& t):TableTennisPlayer(t),rating(r){};
void rate() const
{
cout<<"I'm rating!"<<endl;
}
};
void func(const TableTennisPlayer& T)//括号内参数可用其派生类
{
T.show();
cout<<"This is another function."<<endl;
}
int main()
{
RatedPlayer rplayer1=RatedPlayer("wangjing",true,100);
TableTennisPlayer* tplayerpt = &rplayer1;
tplayerpt->show();//只能用派生类的基类方法
func(rplayer1);
return 0;
}
OUTPUT
I'm wangjing with 1 table.
I'm wangjing with 1 table.
This is another function.
C++有3种继承方式:公有继承,保护继承,私有继承
公有继承:
is-a关系
多态:——具有多种形态
同一个方法在派生类和基态中的行为是不同的。即方法的行为应取决于调用该方法的对象。同一个方法的行为随上下文而异。
实现多态公有继承的方法:
- 在派生类中重新定义基类的方法
- 使用虚方法
virtual:
如果没有使用关键词virtual,程序将根据引用类型和指针类型选择方法,使用后,程序将根据引用或指针指向的对象类型来选择方法。
在声明中不在定义中
注意:
- 如果要在派生类中重新定义基类的方法,应在基类和派生类中都声明为虚的
- 为基类声明一个虚析构函数,virtual ~Stock() 确保释放派生对象时,按正确的顺序调用析构函数。
#include
#include
using namespace std;
class TableTennisPlayer
{
private:
string name;
bool hasTable=false;
public:
TableTennisPlayer()
{
name="none";
}
TableTennisPlayer(string n,bool ht)
{
name=n;
hasTable=ht;
}
virtual ~TableTennisPlayer(){};
virtual void show() const
{
cout<<"I'm "<<name<<" with "<< hasTable <<" table."<<endl;
}
};
class RatedPlayer:public TableTennisPlayer
{
private:
int rating;
public:
RatedPlayer():TableTennisPlayer(),rating(0){};
RatedPlayer(string n,bool ht,int r):TableTennisPlayer(n,ht),rating(r){};
RatedPlayer(int r,const TableTennisPlayer& t):TableTennisPlayer(t),rating(r){};
virtual void show() const
{
TableTennisPlayer::show();//表明作用域限定符
cout<<"I'm a derive and this is my show function."<<endl;
}
};
int main()
{
TableTennisPlayer* d[3];
for(int i=0;i<3;i++)
{
string n;
bool ht;
int rating;
cout<<"name?"<<endl;
cin>>n;
cout<<"has table?"<<endl;
cin>>ht;
cout<<"1 for TableTennisPlayer and 2 for Ratedplayer: ";
int temp;
cin>>temp;
if(temp==1)
{
d[i] = new TableTennisPlayer(n,ht);//注意怎么表达指针⭐
}
else
{
cout<<"rating? ";
int temp;
cin>>temp;
d[i] = new RatedPlayer(n,ht,temp);
}
}
for(int i=0;i<3;i++)
{
d[i]->show();
}
for(int i=0;i<3;i++)
{
delete d[i];//与new对应
}
}
OUTPUT
I'm wangjing with 1 table.
I'm wangjunyi with 0 table.
I'm a derive and this is my show function.
I'm none with 1 table.
I'm a derive and this is my show function.
#include
#include
using namespace std;
class TableTennisPlayer
{
private:
string name;
bool hasTable=false;
public:
TableTennisPlayer()
{
name="none";
}
TableTennisPlayer(string n,bool ht)
{
name=n;
hasTable=ht;
}
void show() const
{
cout<<"I'm "<<name<<" with "<< hasTable <<" table."<<endl;
}
};
class RatedPlayer:public TableTennisPlayer
{
private:
int rating;
public:
RatedPlayer():TableTennisPlayer(),rating(0){};
RatedPlayer(string n,bool ht,int r):TableTennisPlayer(n,ht),rating(r){};
RatedPlayer(int r,const TableTennisPlayer& t):TableTennisPlayer(t),rating(r){};
void rate() const
{
cout<<"I'm rating!"<<endl;
}
};
void func(const TableTennisPlayer& T)//括号内参数可用其派生类
{
T.show();
cout<<"This is another function."<<endl;
}
int main()
{
RatedPlayer rplayer1=RatedPlayer("wangjing",true,100);
TableTennisPlayer* tplayerpt = &rplayer1;
tplayerpt->show();//只能用派生类的基类方法
func(rplayer1);
return 0;
}
基类指针指向派生类:
#include
#include
using namespace std;
class Animal
{
private:
int num;
string name;
public:
Animal(){};
Animal(int n,string na)
{
num=n;
name=na;
}
int getnum()
{
return num;
}
string getname()
{
return name;
}
virtual void eat(){};
};
class Dog:public Animal
{
public:
Dog(int n,string na):Animal(n,na){};
virtual void eat()
{
cout<<this->getnum()<<"号"<<getname()<<"啃骨头"<<endl;
}
};
class Cat:public Animal
{
public:
Cat(int n,string na):Animal(n,na){};
virtual void eat()
{
cout<<this->getnum()<<"号"<<getname()<<"吃小鱼"<<endl;
}
};
int main()
{
int n1,n2;
string na1,na2;
cin>>n1>>na1;
cin>>n2>>na2;
Animal* ptr;
ptr=new Dog(n1,na1);
ptr->eat();
ptr=new Cat(n2,na2);
ptr->eat();
delete ptr;
}
注意虚析构函数
函数名联编:将源代码中的函数调用解释为执行特定的函数代码块。
静态联编(static binding:编译过程完成
动态联编(dynamic binding):在运行时确定。能够在程序运行时选择正确虚函数的代码
向上强制转换(upcasting):派生类引用或指针转换为基类引用或指针,允许
向下强制转换(downcasting):反过来,不允许
静态联编:非虚方法
动态联编:虚方法
静态联编效率更高,为default binding
如果要在派生类中重新定义基类的行为,则将他设置为虚方法,否则设置为非虚方法
- 构造函数不能是虚函数
- 析构函数是虚函数(除非类不用作基类)
- 友元不能是虚函数(成员才能使虚函数,友元不是成员函数)
- 如果派生类没有重新定义函数,将使用该函数的基类版本。
- 重新定义将隐藏方法(基类某个函数所有重载的版本需要全部重新定义)
protected:派生类可访问,客户不可
尽量对类数据成员采用private私有访问控制
纯虚函数 : 提供为实现的方法,声明结尾处=0.
当类声明中包含纯虚函数时,不能创建对象
ABC中至少包含一个纯虚函数
#include
using namespace std;
class accABC
{
private:
string name;
int money;
public:
virtual void withdraw(int m) = 0;
};
template<typename Type>
template<class Type>
更改:
//使用模板成员函数代替原有的类的类方法,每个函数头都以相同的模板声明打头。
template<class T>//
class baseclass
{
private:
int temp;
public:
void show() const;
};
template<class T>//
void baseclass<T>::show() const
{
}