大一下C++学习记录-C Primer Plus版本

第三章 处理数据

3.1简单变量

#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;
}
 

1bytes=8bits

大一下C++学习记录-C Primer Plus版本_第1张图片

第四章 复合类型

4.1数组

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

大一下C++学习记录-C Primer Plus版本_第2张图片


  • STL函数
  1. for_each()
  2. random_shuffle()
    random.shuffle(vec.begin(),vec.end())//改变vec,随机排列其中的元素
  3. sort()
#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;
}

16.1String类

4.2字符串

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.

4.10 vector、array

第5章 循环和关系表达式

5.1for循环

逗号运算符,

#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;

}

字符串的比较

  • C风格字符串
  • string类字符串
#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;

}

5.5循环和文本输入

  • cin忽略空格和换行符,发送给cin的输入被缓冲,只有在按下回车时被发送给程序。
  • cin丢弃换行符
  • cin.get(ch)读取输入的下一个字符(即使是空格),并将其赋值给ch
  • cin.get(ch)/cin.get(char*,ArSize)不丢弃换行符
  • getline(cin.str)读取一行并丢弃换行符
int main()
{
    vector<int> temp;
    int a;
    while(cin>>a)
    {
        temp.push_back(a);
        if(getchar()=='\n')
            break;
    }
}

5.5.4文件尾条件

#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);
    }
}

6.7读取数字的循环

#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

第7章 函数——C++的编程模块

7.3 函数与指针

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;
}

第九章 内存模型和名称空间

9.1单独编译

程序的分解:

  • 头文件:包括结构声明、使用这些结构的函数的声明
  • 源代码文件:包含与结构有关的函数的代码
  • 源代码文件:包含调用与结构相关的函数的代码
//头文件通常包括:函数原型,#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()
{
    
}

9.2存储持续性、作用域和链接性

9.2.3静态持续变量

#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

9.3定位(placement)new运算符

9.4 名称空间

第10章 对象与类

10.1过程性编程和面向对象编程

面向对象编程OOP特性
—抽象
—封装和数据隐藏
—多态
—继承
—代码的可重用性


OOP优点:易维护、易复用、易扩展,可以设计出低耦合的系统,使系统更加灵活和易于维护


用户接口
用户与数据交互的方式有三种:初始化、更新和报告

10.2抽象和类

C++中的抽象:实现抽象接口的类设计
类是一种将抽象转换为用户自定义类型的C++工具,将数据表示和操纵数据的方法组合在一起。类首字母大写


访问控制关键字:protected、public、private


封装:将实现细节与抽象分开成为封装,例如数据隐藏(防止程序直接访问数据)、将类函数定义与类声明放在不同的文件


”::“:作用域解析运算符


C/S模型:客户/服务器模型
客户是使用类的程序,类声明和类方法构成了服务器。
—客户通过以公有方式定义的接口使用服务器
—服务器的设计者可以修改类设计的实现细节,而不能修改接口。

10.3类的构造函数和析构函数

10.3.1constructor and dectructor

为避免成员名和参数名重复,①: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;
}

const 成员函数

//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
}

10.4this指针

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是地址
}

10.5对象数组

要创建类对象数组,必须有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]);
    }
}

10.6类作用域

类作用域:在类中定义的成员变量与成员函数的作用域都为整个类,只在类中已知。

10.6.1 作用域为类的常量(创建一个由所有对象共享的常量)

错误:

#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详解见十二章

10.6.2 作用域内枚举

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);//强制类型转换
}

10.7抽象数据类型

抽象数据类型(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;
        }
    }
}

第11章 使用类

11.1运算符重载

运算符重载是一种形式的C++多态
**函数重载/函数多态:**定义多个名称相同但特征标(参数列表)不同的函数


运算符函数格式

operator op()
operator*()//重载*运算符
district1 = sid + sara;//等价于
district1 = sid.operator+(sara);//implicit invoking sid, explicit invoking sara

11.2 计算时间——一个运算符重载示例

#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.

重载限制:

  1. 重载后的运算符至少有一个是用户定义的类型,防止用户为标准类型重载运算符
  2. 使用运算符时不能违反原来的句法规则。例如不能将%用作%x
    不能修改运算符优先级
    不能创建新运算符
    不能重载以下运算符:
    sizeof
    .
    ::
    ?:
    强制类型转换运算符

11.3 友元

友元:
友元函数
*友元类
*友元成员函数

友元函数
特殊的非成员函数,可以访问类的私有成员
解决t2=t13.75 valid,而t2=3.75t1 invalid的问题

11.3.1 创建友元

//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

11.3.2重载<<运算符

friend ostream& operator<<(ostream& os,Time& t);//按引用传递使用的内存和时间都比按值传递少
ostream& operator<<(ostream& os,Time& t)
{
	os<<t.hour<<" "<<t.minute;
	return os;
}

11.4重载运算符:作为成员函数还是非成员函数

11.5再谈重载:一个矢量类

❗❗❗

11.6类的自动转换和强制类型转换

11.6.1自动类型转换

只含有一个参数的构造函数可作为转换函数

#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.

11.6.2 转换函数

将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)

第12章 类和动态内存分配

12.1动态内存和类

在程序运行时,而不是编译时,确定诸如使用多少内存的问题。

12.1.1复习new delete和静态类成员

new作用:
StringBad* ptr=new StringBad(mottoo);
ptr=new出来的空间
*ptr=mottoo

12.2Stringbad与String

如果没有定义,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)
为了提高处理效率,最简单的方法的重载赋值运算符,使之能够直接使用常规字符串,这样就不用创建和删除临时变量了

12.2.3 使用中括号表示法访问字符


char& String::operator[](int i)//重载中括号访问法
{
    return str[i];
}
// 返回char&,便于:
// String a{"wangjing"};
// a[0]='f';

12.2.4静态类成员函数

静态成员函数后果:

  • 不能通过对象调用静态成员函数。静态成员函数不能用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};

12.2.5

#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

12.3在构造函数中使用new时应注意的事项

三法则(Rule of Three):
在构造函数中使用new时

  • 显式复制构造函数
  • (显式重载构造函数)
  • 显式重载赋值运算符
  • 显式析构函数

new与delete对应,new[]与delete[]对应

12.4对返回对象的说明

12.4.1返回指向cosnt对象的引用

使用const引用的常见原因是旨在提高效率

  • 返回对象将调用复制构造函数,而返回引用不会。
  • 引用指向的对象应在调用函数之前就存在。
  • const String& Max(const String& st1,const String& st2);//const与const对应

12.5使用指向对象的指针

12.6复习各种技术

12.7 队列模拟

第13章 类继承

原始类:基类
继承类:派生类(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;
}

13.1简单基类

派生类特点:

派生类对象存储了积累的数据成员(派生类继承了基类的实现)–>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) {};
};

基类和派生类关系:

  1. 派生类可以使用基类的方法,条件是方法不是私有的。
  2. 基类指针可以在不显式类型转换的情况下指向派生类对象。
  3. 基类引用可以在不显式转换的情况下引用派生类对象。

指针/引用:

  1. 基类指针或引用只能用于调用基类方法
  2. 不可以将基类对象和地址赋给派生类引用和指针
  3. 结果:基类引用定义的函数或指针参数可用于基类对象和派生类对象。
#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.

13.2继承:is-a关系

C++有3种继承方式:公有继承,保护继承,私有继承


公有继承:
is-a关系

13.3多态公有继承

多态:——具有多种形态
同一个方法在派生类和基态中的行为是不同的。即方法的行为应取决于调用该方法的对象。同一个方法的行为随上下文而异。
实现多态公有继承的方法:

  1. 在派生类中重新定义基类的方法
  2. 使用虚方法

virtual:
如果没有使用关键词virtual,程序将根据引用类型和指针类型选择方法,使用后,程序将根据引用或指针指向的对象类型来选择方法。
声明中不在定义
注意:

  1. 如果要在派生类中重新定义基类的方法,应在基类和派生类中都声明为虚的
  2. 为基类声明一个虚析构函数,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;
}

注意虚析构函数

13.4静态联编和动态联编(没认真看)

13.4.1指针和引用类型的兼容性

函数名联编:将源代码中的函数调用解释为执行特定的函数代码块。
静态联编(static binding:编译过程完成
动态联编(dynamic binding):在运行时确定。能够在程序运行时选择正确虚函数的代码
向上强制转换(upcasting):派生类引用或指针转换为基类引用或指针,允许
向下强制转换(downcasting):反过来,不允许


13.4.2虚函数和动态联编

静态联编:非虚方法
动态联编:虚方法
静态联编效率更高,为default binding


如果要在派生类中重新定义基类的行为,则将他设置为虚方法,否则设置为非虚方法

13.4.3虚函数机制

给每个对象增加一个隐藏成员:虚函数表,保存了指向函数地址数组的指针。
大一下C++学习记录-C Primer Plus版本_第3张图片

13.4.4虚函数注意事项

  • 构造函数不能是虚函数
  • 析构函数是虚函数(除非类不用作基类)
  • 友元不能是虚函数(成员才能使虚函数,友元不是成员函数)
  • 如果派生类没有重新定义函数,将使用该函数的基类版本。
  • 重新定义将隐藏方法(基类某个函数所有重载的版本需要全部重新定义)

13.5保护访问控制:protected

protected:派生类可访问,客户不可
尽量对类数据成员采用private私有访问控制

13.6抽象基类(ABC,abstract base class)

纯虚函数 : 提供为实现的方法,声明结尾处=0.
当类声明中包含纯虚函数时,不能创建对象
ABC中至少包含一个纯虚函数

#include
using namespace std;

class accABC
{
private:
    string name;
    int money;
public:
    virtual void withdraw(int m) = 0;
};

13.7继承和动态内存分配

第14章 C++中的代码重用

14.1包含对象成员的类

14.2私有继承

14.3多重继承

14.4类模板

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
{
}

补充重点:文件I/O与异常

你可能感兴趣的:(C++基础,c++)