c++ primer 第七章

析构函数

构造函数:获取资源??

//打开文件
//连接数据库
//动态分配内存 如new

支持写多个(重载)

析构函数:释放资源??

//关闭文件
//断开数据库
//释放内存 如 new

如写了一个(且只能写一个),则先执行我们那个,后执行自动合成的析构函数

合成的析构函数

三法则??

如果写了析构函数,就必须写上复制构造函数和赋值操作符

#include<iostream>
#include<string>
#include<vector>
using namespace std;




class Sales_item
{
public:
    //
private:
        std::string isbn;
        int units_sold;
        double revenue;
 };
/*
没有用到指针,
打开文件
连接数据库
动态分配内存 如new
用自动生成的即可
*/



class NoName
{
public:
    //创建了个对象,用指针来操作这个对象,此情况必须用析构函数
    NoName():pstring(new std::string),i(0),d(0)
    {
        //打开文件
        //连接数据库
        //动态分配内存 如new
        cout << "构造函数被调用了!
" << endl;
    }

    //复制构造函数的声明
    NoName(const NoName &other);
    //赋值操作符的声明
    NoName& operator=(const NoName &rhs);

    //析构函数没有参数,可以写在类外面,这写了声明
    ~NoName();

    

private:
    std::string *pstring;
    int i;
    double d;
};

//复制构造函数
NoName::NoName(const NoName &other)
{
    //创建一个对象赋给指针
    pstring = new std::string;
    *pstring = *(other.pstring);
    i = other.i;
    d = other.d;

}

//赋值操作符
 NoName& NoName::operator=(const NoName &rhs)
{
    //创建一个对象赋给指针
    pstring = new std::string;
    *pstring = *(rhs.pstring);
    i =rhs.i;
    d = rhs.d;
}



//C++规定,右new就会有delete
NoName::~NoName()
{
    //关闭文件
    //断开数据库
    //回收动态分配的内存 如new
    cout << "析构函数被调用了!
" << endl;
    delete pstring;
}


int main() {

    NoName a;//a调用构造函数

    /*
    用new创建对象时,只调用了构造函数
    析构函数未调用,需要用delete删除
    */
    NoName *b = new NoName;//b调用了构造函数
    delete b;//b调用了析构函数

    return 0;//a调用析构函数
}

复制构造函数和赋值操作符

复制构造函数的适用情况??

1.对象的定义形式——复制初始化

//调用了复制构造函数
Sale_item b(a);

2.形参与返回值

//调用参数为string的复制构造函数
Sale_item item = string("123456789");

//temp是函数类部局部变量,不能返回,将temp复制给另外的临时创建的对象,将临时对象返回
Sale_item foo(Sale_item item)
{
    Sale_item temp;
    temp = item;
    return temp;//复制构造函数;  
}

3.初始化容器元素 

cout << endl << "试一下vector: " << endl;
vector<Sale_item> svec(5);

4.构造函数与数组元素

cout << endl << "试一下数组: " << endl;
    Sale_item nmb[] = {
        string("1609103010"),
        string("1609103011"),
        string("1609103012"),
        Sale_item()
    };

赋值操作符

1.重载赋值操作符

2.复制和赋值一起使用

合成的复制构造函数和赋值操作符??

c++自动合成的

定义自己的复制构造函数和赋值操作符??

当类中含有指针数据成员时如*pstring,c++错误处理指针变量,需要自己去写

代码:

#include<iostream>
#include<string>
#include<vector>
using namespace std;

class Sale_item {



public:
    //构造函数
    Sale_item():units_sold(0),revenue(0.0)
    {
        cout << "默认构造函数被调用!";
    }

    Sale_item(const std::string &book)
        :isbn(book), 
        units_sold(0),
        revenue(0.0)
    {
        cout << "构造函数Sales_item(const std::string &book)被调用了!" << endl;
    }

    //复制构造函数
    Sale_item(const Sale_item &orig)
        :isbn(orig.isbn),
        units_sold(orig.units_sold),
        revenue(orig.revenue)
    {
        cout << "复制构造函数被调用了!" << endl;
    
    }

    //赋值操作符
    Sale_item& operator=(const Sale_item &rhs)
    {
        cout << "赋值操作符被调用了!"<<endl;
        isbn = rhs.isbn;
        units_sold = rhs.units_sold;
        revenue = rhs.revenue;
        return *this;
    }

private:
    std::string isbn;
    unsigned units_sold;
    double revenue;
};


Sale_item foo(Sale_item item)
{
    Sale_item temp;//默认的构造函数被调用了
    temp = item;//赋值操作符被调用了
    return temp;//复制构造函数;  temp是函数类部局部变量,不能返回,将temp复制给另外的临时创建的对象,将临时对象返回
}

//此情况没有复制构造函数
//Sale_item& foo(Sale_item item)
//Sale_item foo(Sale_item &item)

class NoName {
public:
    NoName() :pstring(new std::string), i(0), d(0){}

    /*
    赋值构造函数
    c++默认的复制和赋值不能很好的处理指针*pstring
    pstring(other.pstring)指针赋值给指针,错误
    正确做法:指针所指的字符串来创建新的字符串用来初始化这个指针pstring
    */
    NoName(const NoName& other)
        :pstring(new std::string(*(other.pstring))),i(other.i),d(other.d)
    {
        cout << "NonName Copy Constructor" << endl;
    }

    /*
    赋值操作符重载
    c++默认的复制和赋值不能很好的处理指针*pstring
    pstirng = rhs.pstring 指针赋值给指针,错误
    正确做法:指针所指的字符串来创建新的字符串用来初始化这个指针pstring
    */

    NoName& operator=(const NoName &rhs)
    {
        cout << "NoName的赋值操作符";
        pstring = new std::string;
        *pstring = *(rhs.pstring);

        i = rhs.i;
        d = rhs.d;
        return *this;
    }

    //当类中含有指针数据成员时,c++不能处理指针变量,需要自己去写

private:
    std::string *pstring;
    int i;
    double d;
};

int main() {

    NoName x, y;
    NoName z(x);
    x = y;

    Sale_item a;

    //调用了复制构造函数
    Sale_item b(a);

//调用了赋值操作符
    a = b;

    //调用参数为string的复制构造函数
    Sale_item item = string("123456789");

    cout << "试一下foo:";
    Sale_item ret;// 默认的构造函数被调用了!
    ret = foo(item);//复制构造函数被调用了! ,调用结束后, 赋值操作符被调用了!

    cout << endl << "试一下vector: " << endl;
    vector<Sale_item> svec(5);

    cout << endl << "试一下数组: " << endl;
    Sale_item nmb[] = {
        string("1609103010"),
        string("1609103011"),
        string("1609103012"),
        Sale_item()
    };


    return 0;
}

注意了:  复制构造函数在创建新的类时才会被调用,进行对象间的赋值才会调用赋值操作符,前者初始化操作,后者赋值操作

类的作用域:

在类中声明的类型,在main函数中也可以拿来用

class Screen{
public:
    typedef std::string::size_type index;
};

Screen::index get_name()//值得借鉴
{
  return 0;
}
int main()
{
    Screen::index ht;//此方法值得借鉴
    return 0;
}

作用域:怎么使用a全局作用域、b类作用域、c函数作用域??

a. 作用域操作符 ::

b. this->变量

c.变量

int height;
class Screen
{
public:
    typedef std::string::size_type index;
    void dummy_fcn(index height)
    {
        cursor = width * height;//这是哪个height,隐藏
        //cursor = width * this->height;
        //cursor = width * ::height;//使用全局height方法
    }
private:
    index cursor;
    index height, width;
};

构造函数

作用:保证每个对象的数据成员具有合适的初始值

构造函数初始化形式(初始化列表)??

构造函数函数名没有类型,对其数据成员进行初始化,没写的默认初始化,内置类型不进行初始化

默认实参与构造函数??

构造函数永运不需要声明为const,

普通函数看情况

默认构造函数??

构造函数要写,因为类型是内置类型,初始化的时候是不进行默认初始化,默认构造函数将不存在。

初始化列表比赋值效率高??

系统会自动进行初始化,再用this->name = name;则进行一次赋值,效率下降

class Dog
{
public:
    Dog():legs(4)//内置类型不进行默认初始化
    {
        //this->legs = 4;常量必须初始化列表,因为表达式必须是可修改的左值,而常量不可修改
    }
private:
     std::string name;
    // const int legs = 4;
     const int legs;//只能用初始化列表,不能用赋值进行初始化
};

初始化顺序为类数据成员的顺序,而非初始化列表顺序

初始化列表初始化有??

 const类型,引用类型,没有默认构造函数的类类型

隐式类类型转换??(只对于含有一个参数时产生的副作用)

构造函数含有一个参数时,隐式的将参数转变为对象,很可怕,前面加上explicit关键字

理由: C++提供了关键字explicit,可以阻止不应该允许的经过转换构造函数进行的隐式转换的发生。声明为explicit的构造函数不能在隐式转换中使用。

explicit Sale_item(std::istream &is) { is >> *this; }

构造函数的功能合并??

初始化个空值,当对象没有参数是就使用这个空值,有参数,使用初始化列表

class Sale_item{
public:
Sale_item(const std::string &book=" "):isbn(book),units_sold(0),revenue(0,0)
{}
//等同将构造函数Sale_item的功能合并了
private:
    //
};
Sale_item a;
Sale_item a("35535 ")

注意了:调用默认构造函数不要(),Sale_item();是错误的,这是函数的声明了,哈哈

Sale_item *p = new Sale_item();
//或者Sale_item *p = new Sale_item;
delete p;//*p错误,删除地址

类的定义1

类声明->前向声明

类的前置声明,只声明了类,不可以定义个对象,但能定义指针和引用(但没有绑定的对象)

//可以相互定义,前提是声明了Y,因为类X中指针有用到类Y

class Y; //类的前置声明

class X {

//各种成员略
private:
Y *ptr;
};

class Y {

//各种成员略
private:
X *ptr;
X obj;
};

类对象:

Record r("xiaoming");//在堆栈上创建类的对象
Record *p = new Record;//在堆上动态的创建对象 Record *p = new Record;
class Record r2;//沿用c
//C++有严格要求,用new创建对象时,用delete进行删除
delete p;

演变:

C -> C++(带类的C)

c语言中能写的,在C++中都能写

问题:

当有两个相同的构造函数时(名称和参数都相同),出现错误,不知道哦调用哪个

Record(std::string na):name(na)
{}
//显示形参只有一个,却默认初始化有两个
Record(std::string na) :name(na), byte_count(0)
{}

实践代码:

#include<iostream>
#include<string>

using namespace std;

//类的前置声明,不可以定义个对象,但能定义指针和引用(没有对象)
class Y;

class X {

    //各种成员略
private:
    Y *ptr;
};

class Y {

    //各种成员略
private:
    X *ptr;
    X obj;
};

class Screen;

class LinkScreen {

    //Screen window;不可,Screen还未定义,成员
    Screen *window;
    LinkScreen *next;//指向自己
    LinkScreen *prev;

};


//记录
class Record //这是一个完整的类,即是类定义,又是类声明
{

public:
    
    Record():byte_count(0)
    {}
    /*
    已经有一个与之一样的构造函数了,错误
    Record(std::string na):name(na)
    {}
    */
    //显示形参只有一个,却默认初始化有两个
    Record(std::string na) :name(na), byte_count(0)
    {}

    //写在一行也很好
    std::string get_name() const { return name; }
    string::size_type get_byte_count() const { return byte_count; }

private:
    std::string name;
    string::size_type byte_count;

}r3;//传统c语言写法,还有class 换成struct,区别在于默认,c中没有私有成员

int main() {
    Record r("xiaoming");//在堆栈上创建类的对象
    Record *p = new Record;//在堆上动态的创建对象
    class Record r2;//沿用c
    //C++有严格要求,用new创建对象时,用delete进行删除
    delete p;
    cout << r.get_name()<<r.get_byte_count() << endl;
    return 0;
}

类的定义2

将类的数据成员定义为共有成员,可以直接使用,但不符合类的封装原则。

public:
    std::string name;

当函数不修改时,只读不修改数据将其定义为const函数,内容较少时,写在同一行

std::string get_name() const { return name; }
string::size_type get_byte_count() const { return byte_count; }

初始化列表比赋值要快,可读性更好

//显示形参只有一个,却默认初始化有两个
Record(std::string na) :name(na), byte_count(0)
{}
  Screen(index ht=0,index wt=0):content(ht*wt,'A'),cursor(0),height(ht),width(wt)
    {
        //初始化字符串content(ht*wt,'A'),构造函数没写参数没代表没有,默认而以
    }

创建一个函数时候,将框架写出来,return 紧跟其后

bool same_isbn(Sale_item &rh) const
{
  
    
  return          
}

类中的成员,类中可直接调用 

bool same_isbn(Sales_item &rh) const
    {
        //虽然isbn是私有成员,但在类中可以直接调用
        return isbn == rh.isbn;
        /*if (isbn == rh.isbn)
            return 0;
        else
            return -1;*/
    }
#include <iostream>
#include <string>
using namespace std;


class People {
public:
    //构造函数,初始化列表,效率比赋值快
    People(const std::string &nm):name(nm) {

    }
    std::string getName() const
    { 
        return name;
    };

private:
    std::string name;
};

class Sales_item {

public:
    //构造函数
    Sales_item(std::string bookno, unsigned amount, double total) :isbn(bookno), units_sold(amount), revenue(total) 
    { }
    //成员函数,计算平均多少钱
    double avg_price() const
    {
        if (units_sold)
            return revenue / units_sold;
        else
            return 0;
    }
    //成员函数,判断两个销售单是不是同一本数
    bool same_isbn(Sales_item &rhs) const
    {
        //虽然isbn是私有成员,但在类中可以直接调用
        return isbn == rhs.isbn;
        /*if (isbn == rh.isbn)
            return 0;
        else
            return -1;*/
    }
    //将两个销售单进行相加
    unsigned add(const Sales_item &rhs) //不能定义为const
    {
        units_sold +=rhs.units_sold;
        return units_sold ;
          
    }
    unsigned total() {
        return units_sold;
    }
    
private:
    std::string isbn;//书号
    unsigned units_sold;//销售数量
    double revenue;//总金额

};

int main()
{
    /*People m("Wang Dongyu");
    cout << m.getName();*/
    Sales_item b1("95533", 5, 30);
    Sales_item b2("95533", 6, 25);
    if (b1.same_isbn(b2))
        b1.add(b2);
    cout <<b1.total()<<'
'<<b1.avg_price()<<endl;
    return 0;
}

问题一:size_t和size_type区别

为了使自己的程序有很好的移植性,c++程序员应该尽量使用size_t和size_type而不是int, unsigned

1. size_t是全局定义的类型;size_type是STL类中定义的类型属性,用以保存任意string和vector类对象的长度

2. string::size_type 制类型一般就是unsigned int, 但是不同机器环境长度可能不同 win32 和win64上长度差别;size_type一般也是unsigned int
3. 使用的时候可以参考:
   string::size_type  a =123;
   vector<int>size_type b=234;
   size_t b=456;
4. size_t 使用的时候头文件需要 <cstddef> ;size_type 使用的时候需要<string>或者<vector>
5.  sizeof(string::size_type) 
     sizeof(vector<bool>::size_type) 
     sizeof(vector<char>::size_type)  
     sizeof(size_t) 
     上述长度均相等,长度为win32:4 win64:8
6. 二者联系:在用下标访问元素时,vector使用vector::size_type作为下标类型,而数组下标的正确类型则是size_t

类的定义3

同一类型的多个数据成员??

index height, width;

使用类型别名来简化类??

typedef std::string::size_type index;


成员函数可被重载-定义重载成员函数??

char get_cursor()  const;

char get_cursor(index r, index c) const

显法指定inline成员函数??

三种方法,类联函数运行快

i.类中写,默认

ii.函数声明加inline

iii.类外定义前加inline都行,成员函数前指明属于哪个类

问题一:

Screen(index ht=0,index wt=0):content(ht*wt, 'A'),cursor(0),height(ht),width(wt)
{
//初始化字符串content(ht*wt,'A'),构造函数没写参数没代表没有,默认而以
}

问题二:

找不到匹配的构造函数

解决方法1.除去&,2加上const

问题三:何时用&符号??

待解决

#include<iostream>
#include<string>
using namespace std;


class Screen {
//使用类别名进行简化
    typedef std::string::size_type index;
public:
    //构造函数
    /*Screen(const std::string str, index cur, index r, index c):content(str),cursor(cur),height(r),width(c)
    {}*/
    Screen(index ht=0,index wt=0):content(ht*wt, 'A'),cursor(0),height(ht),width(wt)
    {
        //初始化字符串content(ht*wt,'A'),构造函数没写参数没代表没有,默认而以
    }
    //Screen(index ht = 0, index wt = 0, std::string &conts);错误默实参不在形参末尾
    Screen( std::string conts,index ht, index wt);
    //函数声明
    char get_cursor()  const;
    //重载函数,类联函数    
    inline char get_cursor(index r, index c) const;
        
private:
    std::string content; //字符串
    index cursor;//光标位置
    index height, width;//文本框的宽度和高度
};

//类外部构造函数
inline Screen::Screen(std::string conts,index ht, index wt) :content(conts), cursor(0), height(ht), width(wt)
{}

//返回光标位置
char Screen::get_cursor()  const
{
    return content[cursor];
}

//inline char Screen::get_cursor(index r, index c) const
char Screen::get_cursor(index r, index c) const
{
    std::string::size_type row = r * width;
    return content[row + c];
}

int main() {
    Screen a(100,50);
    Screen b("my favorite subject is c++",6,13);
    cout << a.get_cursor() << endl;//a
    cout << a.get_cursor(5, 7) << endl;//a
    cout << b.get_cursor() << endl;//m
    cout << b.get_cursor(0, 6) << endl;//o空格不算

    return 0;
}

隐含的this指针

#include<iostream>
#include<string>
using namespace std;
class Person 
{
public:
    Person(const std::string &na,const std::string &ad):name(na),skill(ad)
    {
        /*
        this->name = na;
        this->skill = ad;
        */
    
    }
    //无需定义this指针,c++隐含,表示指向当前对象*this.name
    std::string get_name() const 
    { 
        return this->name; 
    }
    std::string get_skill() const 
    { 
        return this->skill; 
    }
private:
    std::string name;
    std::string skill;

};

int main() {
    Person hero("红玉", "封喉");
    cout << hero.get_name() << hero.get_skill() << endl;
    return 0;
}

何时使用this指针??

Person(const std::string &name,const std::string &address):name(na),skill(ad)
{
    this->name = name;//若不行this,c++就傻傻分不清
    this->skill = address;
        
}

返回*this??

从const成员函数返回*this??

//返回Screen对象,并能修改
Screen& display(std::ostream &os) { do_display(os); return *this; } //const是个常量,返回Screen对象,但不能修改函数的数据成员
//函数是const,声明类型也要const

基于const的重载??可变数据成员??

思考为什么前面也要加const??

const Screen& dispaly(std::ostream &os) const
{
  ++access_ctr;//const本身不能修改其数据成员,得先声明为 mutable size_t         
    do_display(os)
    return *this

}

友元

三种友元??

1.普通函数

2.类

3.类的成员函数

class Screen {
public:
    //类的友元函数的声明
    friend int caluArea(Screen &);
    //友元类,可以访问类的所有成员
    friend class Window_Mar;
    //声明另外一个类中的部分友元函数
    friend int Dog::foo(Screen&);
};

//友元函数
int caluArea(Screen &screen)
{
    return screen.height * screen.width;
}

//友元类
class Window_Mar
{
public:
    void relocate(int r, int c, Screen &s)//Screen没找到
    {
        s.height += r;
        s.width += c;
    }
};

//类中的部分友元函数
class Dog
{
public:
    int foo(Screen &screen)
    {
        return screen.height*screen.width;
    }

    void koo(Screen& screen) {}
};

类和友元相互依赖,声明和定义,使用了却没有找到定义,该如何处理??

class Screen;
//类中的部分友元函数
class Dog
{
public:
    int foo(Screen &screen);
    void koo(Screen& screen);
};

//省略Screen类

//注意命名空间不要丢
int Dog::foo(Screen &screen)
{
    return screen.height * screen.width;
}
void Dog::koo(Screen& screen){}

实践代码:

#include<iostream>
#include<string>
using namespace std;

/*
虽然加了声明,但还是没有进行定义,友元类无法定义

class Screen;

class Window_Mar
{
public:
    void relocate(int r, int c, Screen &s)//Screen没找到
    {
        s.height += r;
        s.width += c;
    }
};
*/
class Screen;
//类中的部分友元函数
class Dog
{
public:
    int foo(Screen &screen);
    void koo(Screen& screen);
};

class Screen {

public:
    //类的友元函数的声明
    friend int caluArea(Screen & );
    //友元类,可以访问类的所有成员
    friend class Window_Mar;
    /*
    声明另外一个类中的部分友元函数,
    但此时还见不到Dog
    */
    friend int Dog::foo(Screen&);

    typedef std::string::size_type index;
    Screen(int ht=0, int wd=0):contents(ht*wd,' '),cursor(0),height(ht),width(wd)
    {}
    int area() const
    {
        return height * width;
    }
    
private:
    std::string contents;
    index cursor;
    int height, width;
};

//友元函数
int caluArea(Screen &screen)
{
    return screen.height * screen.width;
}

//友元类
class Window_Mar
{
public:
    void relocate(int r, int c, Screen &s)//Screen没找到
    {
        s.height += r;
        s.width += c;
    }
};

//注意命名空间不要丢
int Dog::foo(Screen &screen)
{
    return screen.height * screen.width;
}
void Dog::koo(Screen& screen){}



int main() {

    Screen a(50, 20);
    cout << a.area() << endl;
    cout << caluArea(a) << endl;//1000
    Window_Mar wm;
    wm.relocate(20, 100, a);
    cout << caluArea(a) << endl;//8400
    Dog d;
    cout << d.foo(a) << endl;
    return 0;
}

static 类成员

多用静态成员,少用全局变量、全局函数、全局对象

使用类的static成员的优点??

1.两个类中的静态成员即使同名也互不影响,静态成员的作用域在类中

2.可以私有封装

3.在未创建对象时,就可以对其进行修改

定义static成员??使用类的static成员??static成员函数??static数据成员??

#include<iostream>
#include<string>
using namespace std;

/*
全局变量来保存利率
double interestRate;
此办法不好,所有的类,函数都可以使用
*/
class Account
{
public:
    Account(std::string name,double money):owner(name),amount(money)
    {}
    double getAmount() const
    {
        return this->amount;
    }

    void applyint() { amount += amount * interestRate; }


    void deposit(double money)
    {
        this->amount += money;
    }

/*
如何改变利率,私有成员得通过共有函数修改??
静态变量最好用静态的函数操作!!

静态函数的好处??
类+作用域操作符+函数();未创建对象就能对其修改

静态成员不能使用this指针??
this指针指向当前对象,而静态成员是大家的,不属于任何一个对象
*/
 static double rate() { return interestRate; }
 static void rate(double newRate)
{
    interestRate = newRate;
}




private:
    std::string owner;
    double amount;//a对象有一个,b对象有一个
    static double interestRate;//只有一个
    /*
    double interestRate;
    此办法也不好,当需要修改利率时,得将所有的对象都进行修改
    static double interestRate = 0.015不可以在这里初始化
    特例:
    static const int period = 30;满足三个条件
    */


};


double Account::interestRate = 0.015;

int main() {

    //Account::interestRate = 0.015;错误,私有变量无法访问

    //在未创建对象就可以对其修改,静态成员才可以怎么做
    Account::rate(0.026);
     
    Account a("张三",1000);
    Account b("李四",2000);
    a.deposit(500);
    b.deposit(1000);
    cout << a.rate()<<endl;//0.015
    //通过a修改,b进行查看
    a.rate(0.018);
    cout << b.rate()<<endl;//0.018
    Account::rate(0.02);
    a.applyint();
    b.applyint();
    cout << a.getAmount() << endl;
    cout << b.getAmount() << endl;
    return 0;
}
原文地址:https://www.cnblogs.com/addicted-to-you/p/10745507.html