ps做电商网站尺寸是多少,公司vi包括什么,WordPress 文章最多字,网站 建设文档目录 前言
1.再探构造函数
2.类型转换
3.static成员
4. 友元
5.内部类
6.匿名对象
7.对象拷贝时的编译器优化
结束语 前言 在前面的博客中#xff0c;我们对类的默认成员函数都有了一定了解#xff0c;同时实现了一个日期类对所学的没内容进行扩展延伸#xff0c;本…目录 前言
1.再探构造函数
2.类型转换
3.static成员
4. 友元
5.内部类
6.匿名对象
7.对象拷贝时的编译器优化
结束语 前言 在前面的博客中我们对类的默认成员函数都有了一定了解同时实现了一个日期类对所学的没内容进行扩展延伸本节我们将对类与对象进行大致的最终学习。 1.再探构造函数
• 之前实现构造函数时初始化成员变量 主要使用函数体内赋值构造函数初始化 还有一种方式就是初始化列表初始化列表的使用方式是以一个冒号开始接着是一个以逗号分隔的数据成员列表每个成员变量后面跟一个放在括号中的初始值或表达式。 • 每个成员变量在初始化列表中 只能出现一次语法理解上初始化列表可以认为是每个成员变量定义初始化的地方。 • 引用成员变量const成员变量没有默认构造的类类型变量必须放在初始化列表位置进行初始化否则会编译报错。 #include iostream
using namespace std;
class Time {
public:Time(int hour1): _hour(hour) {cout Time() endl;}
private:int _hour;
};
class Date {
public:Date(int x,int year 1, int month 1, int day 1):_year(year), _month(month), _day(day), _t(12),_ref(x),_n(1) {// error C2512: “Time”: 没有合适的默认构造函数可⽤
// error C2530 : “Date::_ref” : 必须初始化引⽤
// error C2789 : “Date::_n” : 必须初始化常量限定类型的对象
}void Print() const{cout _year - _month - _day endl;}
private:int _year;int _month;int _day;Time _t; // 没有默认构造int _ref; // 引⽤const int _n; // const};
int main() {int x 1;Date d1(x);d1.Print();return 0;
} 上述代码是修改后的正确代码展示 • C11支持在成员变量声明的位置给缺省值这个缺省值主要是给没有显⽰在初始化列表初始化的成员使用的。 • 尽量使用初始化列表初始化因为那些不在初始化列表初始化的成员也会走初始化列表如果这个成员在声明位置给了缺省值初始化列表会用这个缺省值初始化。如果你没有给缺省值对于没有显示在初始化列表初始化的内置类型成员是否初始化取决于编译器C并没有规定。对于没有显示在初始化列表初始化的自定义类型成员会调用这个成员类型的默认构造函数如果没有默认构造会编译错误。 • 初始化列表中按照成员变量在类中声明顺序进行初始化跟成员在初始化列表出现的的先后顺序无关。建议声明顺序和初始化列表顺序保持一致。 #includeiostream
using namespace std;
class Time
{
public:Time(int hour):_hour(hour){cout Time() endl;}
private:int _hour;
};
class Date
{
public:Date():_month(2){cout Date() endl;}void Print() const{cout _year - _month - _day endl;}
private:// 注意这⾥不是初始化这⾥给的是缺省值这个缺省值是给初始化列表的// 如果初始化列表没有显⽰初始化默认就会⽤这个缺省值初始化int _year 1;int _month 1;int _day;Time _t 1;const int _n 1;int* _ptr (int*)malloc(12);
};
int main()
{//对象定义Date d1;d1.Print();return 0;
} 补充题目 下面程序的运行结果是什么D A. 输出 1 1 B. 输出 2 2 C. 编译报错 D. 输出 1 随机值 E. 输出 1 2 F. 输出 2 1 #includeiostream
using namespace std;
class A
{
public:A(int a):_a1(a), _a2(_a1){}void Print() {cout _a1 _a2 endl;}
private:int _a2 2;int _a1 2;
};
int main()
{A aa(1);aa.Print();
} _a2 和 _a1 的初始化顺序不符合它们在类中声明的顺序。这将导致 _a2 使用未初始化的 _a1 值所以输出的_a2是个随机值
2.类型转换
C 支持内置类型如 int、float 等隐式转换为类类型对象只要类中定义了一个接受该内置类型作为参数的构造函数。这种构造函数通常称为单参数构造函数能够允许编译器在需要时自动创建对象。
#include iostream
using namespace std;class MyClass {
public:// 单参数构造函数接受一个 int 类型MyClass(int value) : _value(value) {cout MyClass constructed with value: _value endl;}void Print() const {cout Value: _value endl;}private:int _value;
};int main() {MyClass obj 10; // 隐式转换从 int 到 MyClassobj.Print(); // 输出: Value: 10MyClass anotherObj(20); // 显式构造anotherObj.Print(); // 输出: Value: 20return 0;
} 注意事项 隐式转换的风险: 尽管隐式转换很方便但可能会导致代码的可读性降低尤其是在较大的代码库中。为了避免不必要的隐式转换可以将构造函数声明为 explicit防止不小心的隐式转换 class MyClass { public: explicit MyClass(int value) : _value(value) {} // ... }; 多重构造: 如果类中有多个构造函数确保它们能够明确区分以避免二义性的问题。
3.static成员
• 用 static修饰的成员变量称之为静态成员变量静态成员变量一定要在类外进行初始化。 • 静态成员变量为 所有类对象所共享不属于某个具体的对象不存在对象中存放在静态区。 • 用static修饰的成员函数称之为静态成员函数静态成员函数没有this指针。 • 静态成员函数中可以访问其他的静态成员但是不能访问非静态的因为没有this指针。 • 非静态的成员函数可以访问任意的静态成员变量和静态成员函数。 • 突破类域就可以访问静态成员可以通过类名::静态成员 或者 对象.静态成员 来访问静态成员变量和静态成员函数。 • 静态成员也是类的成员受public、protected、private 访问限定符的限制。 • 静态成员变量 不能在声明位置给缺省值初始化因为缺省值是个构造函数初始化列表的静态成员变量不属于某个对象不走构造函数初始化列表。 #include iostream
using namespace std;
class A {
public:A() {_count;}A(const A count) {_count;}~A() {--_count;}static int getcount() {return _count;}
private://类里面声明static int _count;
};
int A::_count 520;
int main() {cout A::getcount() endl; // 输出520A t1; // _count 增加到 521A t2(t1); // _count 增加到 522cout A::getcount() endl; // 输出522// 此时 t1 和 t2 仍然存在cout t1.getcount() endl;//522cout t2.getcount() endl;//522{A t3(t1); // _count 增加到 523cout A::getcount() endl; // 输出523} // t3 超出作用域, _count 减少到 522cout A::getcount() endl; // 输出522return 0;
}
题目练习
设已经有A,B,C,D 4个类的定义程序中A,B,C,D构造函数调⽤顺序为E 设已经有A,B,C,D 4个类的定义程序中A,B,C,D析构函数调⽤顺序为B C c; int main() { A a; B b; static D d; return 0 } AD B A C BB A D C CC D B A DA B D C EC A B D FC D A B 在全局或局部作用域中构造函数的调用顺序如下
全局和静态对象的构造在程序启动时全局对象如果有和静态对象会首先被构造。局部对象的构造然后当程序进入 main() 函数时局部对象的构造按定义顺序调用。
析构函数的调用顺序与构造函数的顺序相反。析构函数会在对象的生命周期结束时被调用顺序如下
局部对象的析构当程序退出 main() 函数时局部对象按定义的相反顺序析构。全局和静态对象的析构在 main() 函数结束后全局对象和静态对象会被析构。 4. 友元
• 友元提供了一种突破类访问限定符封装的方式友元分为 友元函数和友元类在函数声明或者类声明的前面 加friend并且把友元声明放到一个类的里面。 • 外部友元函数可访问类的私有和保护成员友元函数仅仅是一种声明他不是类的成员函数。 • 友元函数可以在类定义的任何地方声明不受类访问限定符限制。 • 一个函数可以是多个类的友元函数。 • 友元类中的成员函数都可以是另一个类的友元函数都可以访问另一个类中的私有和保护成员。 • 友元类的关系是单向的不具有交换性比如A类是B类的友元但是B类不是A类的友元。 • 友元类关系不能传递如果A是B的友元 B是C的友元但是A不是B的友元。 • 有时提供了便利。但是友元会增加耦合度破坏了封装所以友元不宜多用。 #include iostream
using namespace std;class B;//前置声明
class A {friend void func(const A a, const B b);
private:int _a 520;int _b 1314;
};
class B {friend void func(const A a, const B b);
private:int _a 1314;int _b 520;
};
void func(const A a, const B b) {cout a._a endl;cout b._b endl;
}int main() {A a;B b;func(a, b);return 0;
} #includeiostream
using namespace std;
class A
{// 友元声明friend class B;
private:int _a1 520;int _a2 1314;
};
class B
{
public:void func1(const A aa){cout aa._a1 endl;cout _b2 endl;}void func2(const A aa){cout aa._a2 endl;cout _b1 endl;}
private:int _b1 520;int _b2 1314;
};
int main()
{A aa;B bb;bb.func1(aa);bb.func2(aa);return 0;
}
5.内部类
• 如果一个类定义在另一个类的内部这个内部类就叫做内部类。内部类是一个独立的类跟定义在全局相比他只是受外部类类域限制和访问限定符限制所以外部类定义的对象中不包含内部类。 • 内部类默认是外部类的友元类。 • 内部类本质也是一种封装当A类跟B类紧密关联A类实现出来主要就是给B类使用那么可以考虑把A类设计为B的内部类 如果放到private/protected位置那么A类就是B类的专属内部类其他地方都用不了。 #include iostream
using namespace std;class A {
private:static int _a; // 静态成员int _b; // 非静态成员
public:class B {public:void print(const A a) {cout _a endl; // 访问静态成员cout a._b endl; // 访问非静态成员}};
};int A::_a 520; // 静态成员初始化int main() {cout A类的大小 sizeof(A) endl; // 输出 A 类的大小A::B b; // 创建 B 类的对象A aa; // 创建 A 类的对象b.print(aa); // 调用 print 函数return 0;
} 6.匿名对象
• 用类型(实参) 定义出来的对象叫做匿名对象相比之前我们定义的 类型 对象名(实参) 定义出来的 叫有名对象 • 匿名对象生命周期只在当前一行一般临时定义一个对象当前用一下即可就可以定义匿名对象。 #include iostream
using namespace std;
class A
{
public:A(int a 0):_a(a){cout A(int a) endl;}~A(){cout ~A() endl;}private:int _a;
};class Solution {
public:int Sum_Solution(int n) {//...return n;}
};bool myfunction(int i, int j) { return (i j); }int main()
{A aa1; //有名对象// 不能这么定义对象因为编译器无法识别下面是一个函数声明还是对象定义//A aa2();// 生命周期只在当前一行A(); // 匿名对象A(1);Solution st;cout st.Sum_Solution(10) endl;// 为了更方便cout Solution().Sum_Solution(10) endl;return 0;
} 7.对象拷贝时的编译器优化
• 现代编译器会为了尽可能提高程序的效率在不影响正确性的情况下会尽可能减少一些传参和传参过程中可以省略的拷贝。 • 如何优化C标准并没有严格规定各个编译器会根据情况自行处理。当前主流的相对新一点的编译器对于连续一个表达式步骤中的连续拷贝会进行合并优化有些更新更激进的编译还会进行跨行跨表达式的合并优化。 #include iostream
using namespace std;
class A {
public:A(int a0):_a(a){cout A(int a) endl;}A(const A aa) :_a(aa._a){cout A(const A aa) endl;}A operator(const A aa){cout A operator(const A aa) endl;if (this ! aa){_a aa._a;}return *this;}~A() {cout ~A() endl;}void Print(){cout A::Print- _a endl;}A operator(){_a 100;return *this;}
private:int _a ;
};
void f1(A aa)
{}
A f2()
{A aa(1);aa;cout ########## endl;return aa;
}
int main() {A aa1 ;aa1.Print();//const A aa2 2;//A aa3(aa2);//A aa1;//f1(aa1);//cout endl;// 隐式类型连续构造拷⻉构造-优化为直接构造//f1(1);// ⼀个表达式中连续构造拷⻉构造-优化为⼀个构造//f1(A(2));//cout endl;//cout *********************************************** endl;// 传值返回// 返回时⼀个表达式中连续拷⻉构造拷⻉构造-优化⼀个拷⻉构造 vs2019// ⼀些编译器会优化得更厉害进⾏跨⾏合并优化直接变为构造。vs2022//f2();//cout endl;// 返回时⼀个表达式中连续拷⻉构造拷⻉构造-优化⼀个拷⻉构造 vs2019// ⼀些编译器会优化得更厉害进⾏跨⾏合并优化直接变为构造。vs2022// A aa2 f2();// cout endl;// ⼀个表达式中连续拷⻉构造赋值重载-⽆法优化aa1 f2();cout endl;A ret f2();ret.Print();cout ********* endl endl;//return 0;} 结束语 本节内容到此结束类与对象的学习也暂时告别一段落了希望接下来继续能和大家探讨C的学习最后呢感谢各位友友的支持讲解不足之处也希望大家多多包涵