公司网站建设专家,万能网,网站建设公司起名,旅游网站设计理念文章目录构造函数再探以下代码共调用多少次拷贝构造函数委托构造函数概念形式匿名对象友元友元的声明友元类令成员函数作为友元函数重载和友元注意内部类特性类的const成员可变数据成员类的static成员概念关于static静态成员的类内初始化静态成员能用于某些普通成员不能的场景构…
文章目录构造函数再探以下代码共调用多少次拷贝构造函数委托构造函数概念形式匿名对象友元友元的声明友元类令成员函数作为友元函数重载和友元注意内部类特性类的const成员可变数据成员类的static成员概念关于static静态成员的类内初始化静态成员能用于某些普通成员不能的场景构造函数再探 
以下代码共调用多少次拷贝构造函数 
Widget f(Widget u)
{  Widget v(u);Widget wv;return w;
}int main(){Widget x;Widget yf(f(x));}这道题我第一眼看在调用f的时候会用实参来构造对象u在用对象u构建v,然后用v构建w最后因为是传值返回会用w再构造一个临时量所以调用一次f会使用4次拷贝构造函数最后再用这两次f的返回值来构造y应该是9次。 
但是答案却是7次让我十分不解所以我去论坛搜索了一下发现这里涉及到了编译器的优化。 
我们可以看到在第一次调用f的结束的时候会返回一个由w构造的临时量再将这个临时量作为实参来初始化第二个f的形参u第二个f调用到return这一步时返回一个由w构造的临时量再将这个临时量作为实参来初始化拷贝构造函数的形参编译器觉得这一步有点多余会将其优化为一步第一次直接用w构造u第二次直接用w构造y所以每次调用f的时候其实只经过了3次的拷贝构造最后再加上y的拷贝构造一共是7次。 委托构造函数 
概念 
使用它所属类的其他构造函数执行它自己的初始化过程换言之它把它自己的一些或者全部指责委托给了其他构造函数。 
形式 
有一个成员初始值的列表和一个函数体。成员初始值列表只有一个唯一的入口即类名本身。 
class Date
{
public:// 非委托构造函数Date(int year, int month, int day):_year(year), _month(month), _day(day) {	}// 其余构造函数全部委托给上面的构造函数Date():Date(0,1,1){}Date(int i):Date(i,0,0){}Date(std::istream is):Date(){is  this-_year  this-_month  this-_day;}void pr(){cout  this-_year  ends  this-_month  ends  this-_day  endl;}
private:int _year;int _month;int _day;
};本例中共有四个构造函数。 
第一个构造函数接受三个实参使用这些实参初始化数据成员然后结束工作。 
第二个默认构造函数使用三参数的构造函数完成初始化过程因为函数体为空可知无需再执行其他任务。 
第三个构造函数接受一个实参同样委托给了三参数的构造函数。 
第四个构造函数先委托给了默认构造函数默认构造函数又接着委托给了三参数构造函数。当这些受委托的构造函数执行完后接着执行std::istream is构造函数体的内容。 
ps当一个构造函数委托给另一个构造函数时受委托的构造函数的初始值列表和函数体依次被执行然后控制权才会交还给委托者的函数体。 匿名对象 
当我们想要调用对象中的一个方法或者只是想在这一句使用这个对象其他地方不再使用该对象的时候。如果我们直接构造一个对象使用这无疑是一种很大的浪费因为这个对象我们用了一次就扔了不再需要了而一个对象的生命周期是整个栈帧。 
这时就需要用到匿名对象匿名对象是一种临时对象它的生命周期只有使用它的那一行语句执行完则立即销毁 
class Date
{int _year;int _month;int _day;
public:Date(int year  2020, int month  4, int day  24){_year  year;_month  month;_day  day;cout  gouzao  this  endl;}void print(){cout  this-_year  endl;}~Date(){cout  xigou  this  endl;}
};int main()
{Date d1; //创建一个对象生命周期为整个函数栈帧d1.print();Date().print();//创建一个匿名对象生命周期只有这一行语句实行完则立即调用析构函数Date d2; //创建一个对象生命周期为整个函数栈帧d2.print();return 0;
} 
运行结果  友元 
类中的private部分非类的成员是无法访问的。但类可以允许其他类或者函数访问它的非公有成员方法是令其他类或者函数成为它的友元friend。 
友元函数可访问类的私有和保护成员但不是类的成员函数友元函数不能用const修饰一个函数可以是多个类的友元函数友元函数的调用与普通函数的调用和原理相同 
友元的声明 
友元声明只能出现在类定义的内部但是在类内出现的具体位置不限。友元不是类的成员也不受它所在区域访问控制级别的约束。一般来说最好在类定义开始或结束前的位置集中声明友元。 
友元的声明仅仅指定了访问的权限而非一个通常意义上的函数声明。如果我们希望类的用户能够调用某个友元函数那么我们就必须在友元声明之外再专门对函数进行一次声明。 ps:上述代码想讲的还是要知道友元声明的作用是规定访问权限不能起到普通意义声明的作用。 
友元类 
class A
{friend class Date;//将Date声明为友元类Date可以访问A的所有成员变量
private:string str;
};
class Date
{
public:void Print(){cout  a.str  endl;//访问a的私有成员}
private:int _year;int _month;int _day;A a;
}; 
令成员函数作为友元 
如果不想让整个类作为自己的友元也可以只为那个需要访问自己的private对象的函数提供访问权限。当把一个成员函数声明成友元时必须明确指出该成员函数属于哪个类 
class A;
class Date
{
public:void Print(A);private:int _year;int _month;int _day;
};class A
{friend void Date::Print(A);//将Date的成员函数Print声明为友元Print可以访问A的所有成员变量
private:string str;
};void Date::Print(A a){cout  a.str  endl;//访问a的私有成员
}要想令某个成员函数作为友元我们必须仔细组织程序的结构以满足声明和定义的彼此依赖关系。该例中我们必须按照如下方式设计程序 
首先前向声明A类因为Print的声明会用到定义Date类声明Print函数但不能定义它因为要访问A的私有成员A还没有被定义好无法访问其私有成员定义A类包括对Print的友元声明定义Print此时它才可以使用A的成员 
函数重载和友元 
尽管重载函数的名字相同但它们仍然是不同的函数。因此如果一个类想把一组重载函数声明成它的友元需要对这组函数中的每一个分别声明  在A中并没有关于PrintA, A的友元声明因此其不能访问A的私有成员。 
注意 
1.友元关系是单向的不具有交换性。 Date为A的友元可以访问A的私有成员但是A并不能访问Date的 
2.友元关系不能传递。 如果B是A的友元C是B的友元则不能说明C时A的友元。 内部类 
有没有想过既然类的成员变量可以是自定义类型那能不能在类中再构建一个类呢 
class Date
{
public:void Print(){cout  _year  endl;}private:class A{public:void Print(){cout  _data  endl;}private:int _data;};int _year;int _month;int _day;};可以看到这是可以的但是这两个类有什么关系吗 
这个内部类其实是一个独立的类它不属于外部类同时外部类对它也没有任何特权但是它同时还是外部类的友元类可以通过外部类的对象参数来访问外部类的所有成员。 
特性 
内部类可以定义在外部类的public、protected、private都是可以的。注意内部类可以直接访问外部类中的static、枚举成员不需要外部类的对象/类名。sizeof(外部类)外部类和内部类没有任何关系 类的const成员 
因为对于类和对象封装性是一个很重要的东西但是访问限定符只对外部有影响对自身的成员函数没有影响如果我们不想让一个成员函数对类的成员进行修改这时就需要用const来修饰成员函数。 
class Date
{
public://等价于 void print(const Date* this)void Print() const{cout  _year  -  _month  -  _day  endl;}
private:int _year;int _month;int _day;};对于用const修饰的成员函数需要将const放在最后面来区分开const参数和const返回值。这里的const其实修饰的是该成员函数的this指针所以该成员函数就无法对类的成员进行修改。 const对象可以调用非const成员函数吗 答案不行因为const对象的只能读不能写而非const的成员函数则可读可写使权限放大了不行。  非const对象可以调用const成员函数吗 答案可以因为非const对象的可读可写const成员函数只可读使权限缩小可行。  const成员函数内可以调用其它的非const成员函数吗 答案不行因为const成员函数的this指针是常量只可读而非const的成员函数则可读可写使权限放大了不行。  非const成员函数内可以调用其它的const成员函数吗 答案可以因为非const成员函数的this指针可读可写const成员函数只可读使权限缩小可行。  可变数据成员 
有时会发生这样一种情况我们希望能修改类的某个数据成员即使是在一个const成员函数内。可以通过在变量的声明中加入mutable关键字实现这一目的。 
一个可变数据成员永远不会是const即使它是const对象的成员。因此一个const成员函数可以改变成员的值。 
举个例子我们需要一个可变成员num来追踪每个成员函数被调用了多少次 
class Date
{
public:void Print() const{num;cout  num  endl;}private:mutable size_t num  0;
};int main(int argc, char const *argv[]) {Date d;d.Print();d.Print();d.Print();return 0;
}运行结果  尽管Print是一个const成员函数它仍然能够改变num的值。因为num是个可变成员因此任何成员函数包括const函数在内都能改变它的值。 类的static成员 
概念 
有的时候类需要它的一些成员与类本身直接相关而不是与类的各个对象保持关联。这样的成员叫做类的静态成员。 关于static 
我们通过在成员的声明前加上关键字static使得其与类关联在一起。对于用static修饰的成员函数称为静态成员函数成员变量称为静态成员变量。因为静态的成员的生命域不在类中在静态区所以静态的成员只能在类外初始化。 
静态成员为所有类对象所共享不属于某个具体的实例静态成员变量必须在类外定义定义时不添加static关键字一个静态数据成员只能定义一次静态成员函数可以在类内部也可以在类外部定义在类外部定义时不能添加siatic关键字外部有static关键字表示这个函数在文件内有效类静态成员亦可用 类名::静态成员 或者 对象.静态成员 或者 指向对象的指针-静态成员 来访问静态成员函数不与任何对象绑定在一起它们不包含this指针static成员函数也就不能被声明成const上章提到const是为了将this指针赋予底层const也不能在static函数体内使用this指针不包含this指针也就不能访问任何非静态成员静态成员和类的普通成员一样也有public、protected、private3种访问类别也可以具有返回值静态成员和全局变量虽然都存储在静态区但是静态成员的生命周期只在本文件中而全局变量不是 
因为静态成员函数不属于某个对象所以它没有this指针无法访问任何非静态的成员但是非静态的成员函数具有this指针可以不用通过作用域运算符直接使用静态成员。 
关于第二点和第三点的代码示例 
class Date
{int db  666;static int num;static double init();
public:static int print();void test(){db  db * num;// 成员函数不用通过作用域运算符就能直接使用静态成员}
};
int Date::num  init(); // 定义并初始化一个静态成员int main(int argc, char const *argv[]) {int r;r  Date::print(); // 使用作用域运算符访问静态成员Date d1;Date *d2  d1;r  d1.print(); // 通过Date的对象或引用r  d2-print(); // 通过指向Date对象的指针return 0;
}关于第二点  从类名开始这条定义语句的剩余部分就都位于类的作用域之内了。因此我们可以直接使用init函数虽然其是私有的。 静态成员的类内初始化 
通常情况下类的静态成员不应该在类的内部初始化。然而我们可以为静态成员提供const整数类型的类内初始值不过要求静态成员必须是字面值常量类型的constexpr初始值必须是常量表达式。 
class Date
{constexpr static int num  2*3; // num是常量表达式double db[num];
public:
};如果某个静态成员的应用场景仅限于编译器可以替换他的值的情况则一个初始化的const或者static constexpr不需要分别定义。相反如果我们将它用于值不能替换的场景中该成员必须有一条定义语句。 
如果在类的内部提供了一个初始值则在类外部成员的定义不能再指定一个初始值了  即使一个常量静态数据成员在类内部被初始化了通常情况下也应该在类的外部定义一下该成员。 静态成员能用于某些普通成员不能的场景 
静态数据成员可以是不完全类型声明之后定义之前静态数据成员的类型可以就是他所属的类类型。而非静态数据成员只能声明成他所属类的指针或引用 可以使用静态成员作为默认实参  
非静态数据成员不可以因为它的值本身属于对象的一部分这么做的结果是无法真正提供一个对象以便从中获取成员的值最终将引发错误。  图中error非静态数据成员“Date:test”的使用无效