1.类和结构体,只有的默认访问权限的区别
2.类内不能定义和类外类型名相同的类型名。为什么?typedef机制?
typedef double money; class Account { private: typedef double money;//❌ }
3.变量名一致时的调用(不推荐相同的变量名)
pos height; class Screen { void Screen::dummy_fcn(pos height); private: pos cursor =0 ,height=0, width=0; } void Screen::dummy_fcn(pos height) { cursor=width*height;//传入参数 cursor=width*this->height;//成员 cursor=width*Screen::height;//成员 cursor=width*::height;//全局变量 }
4.初始化和赋值的区别
class ConstRef{ public: ConstRef(int ii); private: int I; const int ci; int &ri; } ConstRef:: ConstRef(int ii){ I=ii; ci=ii;//错误 ri=i; //错误 } ConstRef:: ConstRef(int ii):I(ii),ci(ii), ri(i) { }//正确
5.初始化列表的顺序与参数定义的顺序一致
class X { int i; int j; public: X(int val):j(val),i(j){}//本想val->j->i,实际是:j->i,val->j }
6.默认构造函数、委托构造函数、合成的默认构造函数
class Sales_data{ Sales_data(std::string s, unsigned int, double price): bookNo(s), units_sold(cnt), revenue(int*price) { } Sales_data():Sales_data(" ",0,0){ }//委托构造函数 }
6.1 三五法则
- 三个控制类拷贝的基本操作:拷贝构造函数;赋值拷贝运算符;析构函数
//类值的类 class HasPtr { public: HasPtr(const std::string &s=std::string()): ps(new std::string(s)),i(0) {}//构造函数1 HasPtr(const HasPtr &p):ps(new std::string(*p.ps)),i(p.i){} //构造函数2 HasPtr &operator=(const HasPtr &);//赋值拷贝运算符 ~HasPtr() { delete ps; }//此虚构函数必须同时定义一个对应的构造函数和拷贝复制运算符 private: std::string *ps; int i; };
需要析构函数的类也需要赋值和拷贝操作;
需要赋值操作的类也需要拷贝操作,反之亦然;
HasPtr f(HasPtr hp)//传值,拷贝 { HasPtr ret = hp;//再拷贝 return ret;//ret和hp都被销毁 } HasPtr p("something"); f(p);//f结束,p.ps被销毁 HasPtr q(p);//p 和 q都指向无效内存---------------->如何解决?
7.默认初始化、值初始化
7.1 默认初始化
- 不使用初值定义非静态变量 int i;
- 类成员为类类型,使用合成的默认构造函数时?
- 没有在构造函数中显示初始化时
7.2 值初始化
- 数组初始化中,提供的值数量少于数组大小
- 不使用初始值定义局部静态变量
- 通过T()显式求是初始化
8.当类只含有内置类型或复合类型的成员时,编译器是不会为类合成默认构造函数的。因为:内置类型可以有自己的默认初始化,复合类型有给出的显示的构造函数进行初始化(如果没给,而复合类型中的成员又不是内置类型,说白了,找不到初始值的话,就会报错)
8.1 B()没有给b_member初值
8.2 去NoDefault里找初值。发现NoDefault的构造函数里也没有给初值:报错
9.聚合类
所有成员public,没有构造函数,没有类内初值,没有基类,没有virtual函数
10.字面值常量类
11.类的静态成员
- 静态成员可以是不完全类型,可以是类本身(指针也可以,但普通数据成员不行)
- 静态成员可以作为默认实参(因为可以提取值,但普通成员不行)
12.析构
- 析构函数不接受参数,不能被重载
- 隐式销毁一个内置指针类型,不会delete它所指向的对象(智能指针会?)
- 如果一个类需要自定义析构函数,那它也需要自定义拷贝构造函数、拷贝赋值运算符
13.阻止拷贝-delete(不用private是因为友元可以访问)
struct NoCopy { NoCopy() = default; //使用合成的默认构造函数 NoCopy(const NoCopy&) = delete; //阻止拷贝//不要声明为private NoCopy &operator = (const NoCopy&) = delete;//阻止赋值//不要声明为private,成员函数、友元函数调用会出错 ~NoCopy() =default;//如果是delete,就不能销毁其对象了 };
- 类成员的析构函数是delete或private,则合成的析构函数将会是delete
- ------------拷贝构造------------------------,----------拷贝构造------------------
- ------------析构函数------------------------,----------拷贝构造------------------
- ------------拷贝赋值运算符是delete或private或const &,----------拷贝赋值运算符----------------
- ------------析构函数是delete或private,或有引用成员且没有类内初始化器,或有const成员且类型未显示定义默认构造函数,则合成的默认构造函数将会是delete
- 析构函数不能是delete
13.拷贝控制和资源管理:
13.1 行为像值的类:有自己的状态,完全独立。
//类值的类 class HasPtr { public: HasPtr(const std::string &s=std::string()): ps(new std::string(s)),i(0) {}//构造函数1 HasPtr(const HasPtr &p):ps(new std::string(*p.ps)),i(p.i){} //构造函数2 HasPtr& operator=(const HasPtr &);//赋值拷贝运算符 ~HasPtr() { delete ps; }//此虚构函数必须同时定义一个对应的构造函数和拷贝复制运算符 private: std::string *ps; int i; }; HasPtr& HasPtr::operator=(const HasPtr &rhs) { auto newp = new string(*rhs.ps);//为了避免rhs和*this是同一个对象 delete ps; ps = newp; i = rhs.i; return *this; }
13.2 行为像指针的类:共享状态,改变副本即改变原值。(对原值进行不同状态下的操作)
最好的办法:shared_ptr
直接管理资源:引用计数(存放在哪里——动态内存)
//类指针的类 class HasPtr { public: HasPtr(const std::string &s=std::string()): ps(new std::string(s)),i(0),use(new std::size_t(1)) {}//构造函数1 HasPtr(const HasPtr &p) :ps(new std::string(*p.ps)), i(p.i), use(p.use) { ++*use; } //构造函数2 HasPtr& operator=(const HasPtr &);//赋值拷贝运算符 ~HasPtr() { if (--*use == 0) { delete ps; delete use; }//此虚构函数必须同时定义一个对应的构造函数和拷贝复制运算符 } private: std::string *ps; int i; std::size_t *use;//引用计数 }; HasPtr& HasPtr::operator=(const HasPtr &rhs) { ++ *rhs.use;//增加右侧对象的引用计数 if (--*use == 0)//先减少原本对象的引用计数(可能本来是别的对象,更改之后就要把原来的减一) { delete ps; delete use; } //拷贝 ps = rhs.ps; i = rhs.i; use = rhs.use; return *this; }
涉及资源管理的操作:构造、赋值、析构、交换(适合指针)