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 << "构造函数被调用了!\n" << 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 << "析构函数被调用了!\n" << 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()<<'\n'<<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; }
posted on 2019-04-25 09:06 浪漫的程序猿Plus 阅读(380) 评论(0) 编辑 收藏 举报