资讯动态

类和对象(3)

发布时间:2026/10/26 2:02:02 来源:尧图企业网站定制
1.构造函数1. 之前实现构造函数时初始化成员变量主要是在函数体内赋值除此之外构造函数还有一种初始化方式即初始化列表。初始化列表以冒号开始后面跟一个以逗号分隔的成员变量列表每个成员变量后面用括号给出它的初始值或表达式。class Date { public: Date(int x, int year 1, int month 1, int day 1) :_year(year) , _month(month) , _day(day) { } void Print() const { cout _year - _month - _day endl; } private: int _year; int _month; int _day; };2. 每个成员变量在初始化列表中只能出现一次。Date(int x, int year 1, int month 1, int day 1) :_year(year) , _month(month) , _month(2)//不对 , _day(day) { }3. 引用成员变量、const 成员变量以及没有默认构造函数的类类型变量必须在初始化列表中进行初始化否则会编译报错。这三类变量的共同点是定义时必须初始化。实例化Date d1时进入构造函数体之前编译器按成员声明顺序逐个构造成员变量诞生。构造函数体里只能做赋值不能做初始化。普通成员int、有默认构造的类能在这一步被默认构造出来所以函数体里可以再赋值。而引用、const、无默认构造的类在构造诞生这一刻就必须拿到初值/绑定/指定构造参数无法被默认构造出来所以必须在初始化列表里指定——初始化列表正是用来决定阶段1 每个成员怎么构造的。4. 无论是否显式写出初始化列表每个构造函数都有初始化列表无论是否在初始化列表中显式初始化成员变量每个成员变量都要经过初始化列表初始化。5.初始化列表中按照成员变量在类中声明顺序进⾏初始化跟成员在初始化列表出现的的先后顺序⽆ 关。建议声明顺序和初始化列表顺序保持⼀致。6.初始化方式初始化列表缺省值函数体内初始化(做一些初始化列表写不下的判断等)class Time { public: Time(int hour) :_hour(hour) { cout Time() endl; } private: int _hour; }; class Date { public: Date(int x, int year 1, int month 1, int day 1) : _month(month) , _day(day) , _t(12) , _ref(x)//引用成员变量_ref //,_ref(year)//这样给值不行year是局部变量_ref是引用在函数外还要用 //只能给赋值一个全局变量或者 // 用int xx与_ref都是xx的别名 , _n(1) { } void Print() const { cout _year - _month - _day endl; } private: //声明 int _year 2000;//缺省值 int _month 11; int _day 21; Time _t;// 没有默认构造的类类型变量 //默认构造不写编译器自动生成的无参全缺省 //都没有那就是没有默认构造 int _ref; // 引用必须在初始化列表初始化 const int _n; // const 必须在初始化列表初始化 }; int main() { int xx 0; Date d1(xx, 2026, 10, 24); xx; return 0; }7.题目下⾯程序的运⾏结果是什么DA. 输出1 1 B. 输出2 2 C. 编译报错 D. 输出1 随机值 E. 输出1 2 F. 输出2原因初始化列表中按照成员变量在类中声明顺序进⾏初始化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(); }2.类型转换一C语言只能在这些基本类型内置类型之间转int转short整形之间int转double整形和浮点之间int转int*整形和指针之间int*转double*指针之间二C类型转换1.C⽀持内置类型隐式类型转换为类类型对象需要有相关内置类型为参数的构造函数。class A { public: A(int a1) :_a1(a1) {} void Print() { cout _a1 _a2 endl; } private: int _a1 1; int _a2 2; }; int main() { A aa1 1; aa1.Print(); const A r1 1;//为什么要加const因为隐式类型转换生成的临时对象具有常性而r1要引用这个具有常性的临时对象。 return 0; }1区分构造与拷贝构造。A aa1(1); // 构造函数直接初始化 A aa1 1; // 拷贝构造初始化 右边先被转换成同类型的东西再拿来构造左边。编译器看到 1 是 int而左边是 A类型不匹配于是找能不能把 int 变成 A2为什么A aa1 1;不加const就可以const A r1 1;必须加const本质是先构造一个临时对象temp再调用拷贝构造函数将类的成员变量值拷贝给aa1temp对象与aa1对象是两块不同的存储空间不涉及到权限放大问题。但是r1是对临时对象temp的引用本质上操作的是同一块内存空间所以会产生权限放大问题必须加const。3为什么要这么设计(加隐式类型转换)呢class A { public: A(int a1) :_a1(a1) {} void Print() { cout _a1 _a2 endl; } private: int _a1 1; int _a2 2; }; class Stack { public: void Push(const A aa) //用引用传参减少拷贝传值传参会先调用拷贝构造生成临时对象temp //为什么要加constaa2传递进来没事因为是传引用 //但是2传递进来会发生隐式类型转换隐式类型转换会产生临时对象临时对象具有常性 { } private: }; int main() { Stack st; A aa2(2); st.Push(aa2); st.Push(2); return 0; }没有隐式类型转换得写 A aa2(2); st.Push(aa2);有了之后只用写st.Push(2);4编译器会做 拷贝消除 优化发现 temp 只是为了构造 aa1 而生于是直接在 aa1 的内存上调用 A(1)跳过临时对象和拷贝构造。C17 起这是强制的。5支持多参数隐式类型转换class A { public: A(int a1,int a2) :_a1(a1) ,_a2(a2) {} void Print() { cout _a1 _a2 endl; } private: int _a1 1; int _a2 2; }; class Stack { public: void Push(const A aa) { } private: }; int main() { A aa3 { 1,1 }; const A r2 { 2,2 }; Stack st; st.Push({ 3,4 });//隐式类型转换 return 0; }2.构造函数前⾯加explicit就不再⽀持隐式类型转换。class A { public: explicit A(int a1) :_a1(a1) {} void Print() { cout _a1 _a2 endl; } private: int _a1 1; int _a2 2; };3.类类型的对象之间也可以隐式转换需要相应的构造函数⽀持。class A { public: A(int a1) :_a1(a1) {} A(int a1,int a2) :_a1(a1) ,_a2(a2) {} void Print() { cout _a1 _a2 endl; } int Get() const { return _a1 _a2; } private: int _a1 1; int _a2 2; }; class B { public: B(const A a)//它的参数就是一个A类型 :_b(a.Get()) {} private: int _b 0; }; int main() { A aa1 1; B b1 aa1; return 0; }3.static成员⽤static修饰的成员变量称之为静态成员变量静态成员变量⼀定要在类外进⾏初始化。静态成员变量为所有类对象所共享不属于某个具体的对象不存在对象中存放在静态区。⽤static修饰的成员函数称之为静态成员函数静态成员函数没有this指针。静态成员函数中可以访问其他的静态成员但是不能访问⾮静态的因为没有this指针。⾮静态的成员函数可以访问任意的静态成员变量和静态成员函数。突破类域就可以访问静态成员可以通过类名::静态成员或者对象.静态成员来访问静态成员变量和静态成员函数。静态成员也是类的成员受public、protected、private访问限定符的限制。静态成员变量不能在声明位置给缺省值初始化因为缺省值是个构造函数初始化列表的静态成员变量不属于某个对象不⾛构造函数初始化列表class A { public: A()//只有构造和拷贝构造会产生对象它两函数即可 { _scount; } A(const A t) { _scount; } ~A() { --_scount; } //静态成员函数没有this指针 static int GetACount() { //_a1;//不能访问非静态成员变量因为没有this指针 return _scount; } int func()//非静态成员函数可以访问任意的静态成员变量和静态成员函数 { _scount; return _scount; } private: // static不走初始化列表因为储存在静态区属于类内所有对象不是aa1或者aa2具体哪一个独有如果走静态区那不成独有的了 //放在全局int _scount可以所有类共用_scount这个变量但是谁都可以访问它不安全 //类里面声明static声明时不能给缺省值 static int _scount; }; // 类外面初始化 int A::_scount 0; int main() { cout A::GetACount() endl; A a1, a2; A a3(a1); //访问静态成员函数的两种方法 cout A::GetACount() endl; cout a1.GetACount() endl; //cout A::_scount endl; return 0; }题目class Sum { public: Sum() { _ret _i; _i; } static int GetRet() { return _ret; } //为什么int Get_ret()前面要加static //因为在这个代码里调用它的方式是 Sum::Get_ret()直接用类名调用而不是通过某一个具体的 对象调用。 //在 C 中普通的成员函数必须通过对象来调用比如 arr[0].Get_ret()而静态成员函数属于整个类可以直接用 类名::函数名() 来调用。 //如果没有加 static编译器会直接报错说“必须通过对象来调用非静态成员函数”。 private: static int _i; static int _ret; }; int Sum::_i 1; int Sum::_ret 0; class Solution { public: int Sum_Solution(int n) { // 变⻓数组 Sum arr[n];//就像是向编译器下达了一个命令“请给我连续造 n 个 Sum 类型的房子”。 return Sum::GetRet(); } };4.友元友元提供了⼀种突破类访问限定符封装的⽅式友元分为友元函数和友元类在函数声明或者类 声明的前⾯加friend并且把友元声明放到⼀个类的⾥⾯。外部友元函数可访问类的私有和保护成员在类外不能访问private下的变量友元或者getmonth函数可以解决在类外不能访问的问题友元函数仅仅是⼀种声明他不是类的成员函数。它在public底下只是声明真正定义在类外友元函数可以在类定义的任何地⽅声明不受类访问限定符限制。(放在public底下private底下都可以)⼀个函数可以是多个类的友元函数。友元类中的成员函数都可以是另⼀个类的友元函数都可以访问另⼀个类中的私有和保护成员。// 前置声明否则A的友元函数声明编译器不认识B class B; class A { // 友元声明 friend void func(const A aa, const B bb); private: int _a1 1; int _a2 2; }; class B { // 友元声明 friend void func(const A aa, const B bb); private: int _b1 3; int _b2 4; }; void func(const A aa, const B bb) { cout aa._a1 endl; cout bb._b1 endl; } int main() { A aa; B bb; func(aa, bb); return 0; }class A { //可以把B的成员函数声明成A的友元函数但这样比较麻烦 // 使用友元声明类 friend class B; private: int _a1 1; int _a2 2; }; class B { public: void func1(const A aa)//想在B中访问类A的私有B变成A的友元B是A的朋友B可以访问A的私有成员变量 { cout aa._a1 endl; cout _b1 endl; } void func2(const A aa) { cout aa._a2 endl; cout _b2 endl; } private: int _b1 3; int _b2 4; }; int main() { A aa; B bb; bb.func1(aa); bb.func1(aa); return 0; }友元类的关系是单向的不具有交换性⽐如A类是B类的友元但是B类不是A类的友元。友元类关系不能传递如果A是B的友元B是C的友元但是A不是C的友元。有时提供了便利。但是友元会增加耦合度破坏了封装所以友元不宜多⽤。5.内部类如果⼀个类定义在另⼀个类的内部这个内部类就叫做内部类。内部类是⼀个独⽴的类跟定义在 全局相⽐他只是受外部类类域限制和访问限定符限制public,private所以外部类定义的对象中不包含内部类。内部类默认是外部类的友元类。内部类本质也是⼀种封装当A类跟B类紧密关联A类实现出来主要就是给B类使⽤那么可以考 虑把A类设计为B的内部类如果放到private/protected位置那么A类就是B类的专属内部类其 他地⽅都⽤不了。class Solution { class Sum//Sum类受到private访问限制符限制在Solution类外不能访问避免了类外调用Sum产生危险 { public: Sum() { _ret _i;//Sum是Solution的友元可以访问Solution的私有成员变量_i,_ret _i; } }; public: int Sum_Solution(int n) { Sum arr[n];//上一题那种写法每调用一次Sum就会增加一次比较危险放在类内安全很多 return _ret;//不用写Get函数_ret就是自己的成员变量 } private: static int _i; static int _ret; }; int Solution::_i 1; int Solution::_ret 0;不支持变长数组可以用Sum* arr new Sum(n);替代

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑