ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

primer 类初探

2022-05-08 23:35:08  阅读:146  来源: 互联网

标签:const 函数 int 对象 初探 test primer 指针


  1 不要返回局部变量的引用或者指针;
  2 因为局部变量在函数调用完就消亡了,此时引用的对象或指针指的内存都被清空了
  3 这时候再使用就可能出问题;
  4 
  5 没有名字的对象叫临时变量;
  6 
  7 函数返回值为引用时此时为左值,其他类型为右值;
  8 这意味着可以对函数调用赋值;而右值是常量不能被赋值; 
  9 如:
 10 int &test(){}
 11 test() = 10;
 12 
 13 返回数组指针的时候;
 14 可以用别名
 15 如:
 16 typedef int p[10]; 
 17 p* func(int i) //此时函数返回值指向10个元素的数组指针
 18 
 19 如果不用别名,正常的是如int (*p) [10] = &a;
 20 数组维度要在函数后面;
 21 如 int (*func(int i)) [10] //表示函数的返回值是指针,指向一个int [10 的数组
 22 c++11为我们提供后置返回值,比较贴近逻辑;
 23 auto func(int i) -> int (*) [10] //
 24 
 25 
 26 main函数不能重载
 27 重载函数,形参数量或者类型有所不同 
 28 形参为顶层const时,此时顶层const无法与普通形参区分开
 29 如 void test(const int a);
 30    void test(int a);//test(10)此时编译器无法区分;
 31 底层const 则是重载
 32 如 void test(int &a);
 33    void test(const int &a)//此时可以通过判断1绑定的是普通变量,2绑定的是常量
 34 本质上不一样;重载形参类型看的是本质而不是表面
 35  
 36 默认参数匹配规则
 37 默认参数全部必须靠右,不能乱序; 
 38 如:
 39 void test(int a = 1, int b = 2, string s = "hello"){}
 40 如果想要覆盖s,则前面两个参数必须填上;
 41 调用test(1,2);
 42 一旦某个参数启动默认,后面必须全部都是默认,否则报错;
 43 如test(,2,)//从第一个参数默认,后面还传2进来,故报错; 
 44 函数调用前包含一系列工作:要先保存寄存器,并在返回时恢复;可能要拷贝实参等等;
 45 如果是简单功能的函数我们可以声明成内联函数,这样就可以直接展开省去函数调用,而又保留了函数的泛型,封装性
 46 
 47 
 48 constexpr函数与constexpr指针
 49 被constexpr修饰的对象相当于顶级const;
 50 例如constexpr int a = 10;//a是一个常量
 51 constexpr int * p = &r;//p是一个常指针等价于 int *const p = &r;
 52 
 53 constexpr函数它的返回值不一定是常量表达式,取决于函数逻辑是怎么样返回的;
 54 基于上述原因,constexpr函数一般要求形参类型是字面值类型,而且函数有且只有一条return语句;
 55 
 56 由于内联函数和constexpr函数和其他函数不同,Ibanez都是比较短的函数
 57 由于只有函数声明,没有定义是不能展开的,基于分离式编译,所以内联函数和constexpr函数一般定义在头文件//这才是有用的;
 58 
 59 assert预处理宏;
 60 用法:
 61 assert(expr)
 62 如果expr为真,则什么也不做,如果expr为假,则输出信息并终止程序
 63 assert宏定义在cassert头文件,预处理名字由预处理编译器而非编译器管理
 64 所以我们能直接使用assert名字而不是std::assert,也不需要为assert使用using说明;
 65 这就是预处理编译器的强大;但是宏名字必须唯一。
 66 
 67 可以引用#NODEBUG宏来跳过断言
 68 用法:
 69 #define NDEBUG
 70 #include<bits/stdc++.h>
 71 using namespace std;
 72 
 73 inline void test(int a )
 74 {
 75     cout << a  << "\n";
 76 }
 77 
 78 int main()
 79 {
 80     int b = 9;
 81     test(b);
 82     assert(0);//如果没有NDEBUG宏这里会抛出断言,NDBUG宏必须在assert头文件前!!! 
 83     return 0;
 84 }  
 85 
 86 也可以自定义断言;
 87 void print(const int ia[], size_t size)
 88 {
 89     #ifndef NDEBUG
 90     cerr << __func_ << "array size is:" << size << "\n";
 91     #endif
 92     //__func__是一个存放函数名的内置变量,它里面是链接器链接函数时需要找的名字;
 93     //__FILE__ 存放文件名的字符串字面值;
 94     //__LINE__存放当前行的整型字面值
 95     //__TIME__存放文件编译时间的字符串字面值
 96     //__DATE__存放文件编译日期的字符串字面值;     
 97 } 
 98  
 99 
100 函数指针
101 bool mycmp(const string &s1, const string &s2);
102 
103 用指针代替函数名字即可
104 bool (*p)(const string &, const string &); 
105 (*p)是一个指针,指向一个返回值为bool的函数
106 函数名被使用时自动转变成函数指针; 
107 bool b1 = mycmp("hello", "h");
108 bool b2 = p("1", "2");
109 
110 和数组一样函数返回值不能是数组,函数返回值也不能是函数,但是可以是指针
111 返回值是函数指针;
112 
113 using F = int (int, int);//F是函数类型;
114 using PF = int (*) (int, int);//PF是函数指针;
115 
116 PF f(int);//正确,f是一个函数返回值为函数指针;
117 F *f(int);//正确,等价上面式子
118 也可以用强大尾置返回类型,这种比较符合逻辑
119 auto f(int) -> int (*) (int, int);
120 
121 
122 
123 类:
124 
125 成员函数的声明必须在类内,定义可以在类外也可以在类内;
126 
127 this指针与常量
128 调用机制:
129 有个类
130 class test{
131     public:
132     int a;
133     int print(){return a;}
134 };
135 test mytest;
136 当调用mytest.print();时等价于 test::print(&mytest) const {return this -> a;}
137 这里this是一个隐式形参,编译器负责把对象地址传给隐式this;
138 this总是指向这个对象,所以this是一个常指针;
139 
140 this指针默认是 test *const,常指针,这意味着它不能绑定一个常对象;
141 这样的话,如果对象是常对象,我们就不能用this指针,即什么调用也干不了,但是我们必须要调用啊
142 所以约定在函数参数列表之后用const表示this是一个指向常量的指针
143 即int print() const {};
144 这样的话隐式的this就变成了const test *const指针了;
145 这样我们就能愉快的绑定常对象成员了,这也就是为什么常对象成员只能调用常成员函数和对象!!!
146 
147 穿插volatile变量和mutable变量
148 volatile变量:为了避免编译器优化,对经常使用的对象放到寄存器,如果该对象是指针,则指针的内容会一直是寄存器里的内容
149 而不是内存的内容,因为该指针的内容有可能会变,这时候需要给对象加上volatile关键字告诉编译器不要对它优化!
150 
151 mutable变量:配合常成员函数使用。
152 如:我们想要在常成员函数中改变一个值,但是此时this是双const,如果我们把函数参数列表后的const去掉,那么我们就不能泛型绑定对象了
153 此时我们只要把想要改变的变量加上mutable关键字,告诉编译器我们暂时把这个变量和this的底层Const去掉,这样this就能在这时修改了;
154 
155 这里补充一点是,常成员函数绑定的this指针在调用类成员变量时能知道该this类型是cons type *const;
156 而在调用函数时,函数是类型共享一份的,所以不清楚此时this绑定的对象是不是常对象,所以要在函数后面加const把this也变成双const;
157 
158 所以常对象可以随便调用类成员变量和常成员函数;
159 普通对象可以随便调用成员函数;
160 最后总结:常量对象,常量对象的引用或指针都只能调用常成员函数;
161 
162 类的作用域下,变量的顺序可以随意摆放,而被成员函数使用;
163 原因是编译器在处理类时,先编译成员变量,然后再编译成员函数;成员变量和函数其实是分离的;
164 
165 类内声明函数,类外定义函数粗腰保持一直,如果是由const属性也必须包含
166 如:
167 double A::print() const
168 {
169     cout << a << "\n"    
170 }
171 解释:这是一个A类的常成员函数,返回值为double,其中a是成员变量,可以直接隐式调用this - > a; 
172 因为函数默认会有个隐式形参this指针; 所以a就是this - > a; 不是什么参数都没就能调用;
173 
174 返回this对象的函数
175 
176 type & type::add(const type &obj)
177 {
178     a += obj.a
179     b += obj.b;
180     return *this;
181 }
182 这样做的好处是在当前对象修改,因为是引用所以省去了返回值的拷贝;
183 
184 对自定义的类进行读入读出;
185 istream &read(istream &is, type &obj)
186 {
187     is >> obj.a >> obj;
188     return is;    
189 } 
190 //这样我们就能读入一个type对象了,也可以read(cin,obj) >> othertype; 
191 同理
192 ostream &print(ostream &os, type &obj)
193 {
194     os << obj.a << obj.b;
195     return os;    
196 } 
197 注意:IO类属于不能被拷贝的类型,所以只能用引用,因此也只能返回引用,如果不是引用会产生拷贝;
198 
199 
200 构造函数:用于初始化类对象的数据成员; 
201 构造函数名字和类名一致,而且没有返回值;
202 构造函数不能被声明成cosnt,因为这样this指针就不能修改成员变量,也就无法对它们赋值了;
203 
204 默认构造函数无须任何实参,而且只有当类没有任何构造函数时,编译器才会生成默认构造函数; 
205 如果是类内成员变量有初始值,则初始值不变,如果没有赋值而且是内置类型,则按照内置类型规则进行初始化; 
206 
207 class type{
208     type() = default;//告诉编译器需要默认构造函数 
209     type(const std::string &obj1, int32_t obj2) : s(obj1), a(obj2) {}//构造函数初始化列表 
210 }; 
211 类外定义构造函数
212 type::type(int obj){a = obj};//这里解释一下第一个type::表示是type类,第二个type是构造函数的名字与l类名一样;
213 
214 
215 拷贝、赋值和析构 
216 拷贝:初始化变量以及以值的方式传递(传参数)或者返回一个值,就会发生拷贝;
217 如: int a(10);void test(type obj); return obj;
218 赋值:使用赋值运算符"="时会发生赋值;
219 析构:对象销毁时编译器会自动调用析构函数;
220  
221 如果我们不定义这些,编译器会默认替我们生成:构造、拷贝、赋值、析构函数;
222 
223 访问控制与封装
224 常说的多态、继承、封装
225 封装就是使用访问说明符 public: private: protect: 
226 public:可以被该类中的函数、子类的函数、友元函数访问,也可以由该类的对象访问;
227 protected:可以被该类中的函数、子类的函数、友元函数访问,但不可以由该类的对象访问;
228 private:可以被该类中的函数、友元函数访问,但不可以由子类的函数、该类的对象、访问。
229 
230 struct 和 class唯一区别就是struct默认访问控制是public,class默认访问控制是private;(什么控制符都没声明的情况下)
231 
232 类内初始化只用 =赋值或者用{}初始化而不用() 
233 因为怕出现
234 class Widget 
235 {
236 private: 
237   typedef int x;
238   int z(x);//此时z被当成一个函数而不是初始化!!! 
239 };
240 
241 一个类在未完全定义前,不能使用它,但可以定义它的指针和引用;
242 因为未完全定义,编译器不知道这个类有哪些成员,以及需要分配多少内存
243 因为指针变量是存储定制,默认四字节,所以只要不调用就没影响
244 而引用指的是传参或返回值,不是type &a = b这种;
245 声明是表示存在,而定义是给声明做具体解释,需要知道成员和内存信息;
246  
247 class type{
248     type *p;
249     type& test();//只能声明不能定义; 
250 }; 
251 
252 友元类
253 class type
254 {
255     friend class type2;    
256 };
257 友元没有传递性
258 上面type2作为type的友元类
259 假设type2里有一个友元类type3;
260 则type3不是type的友元类;
261 
262 其他类的函数声明成友元函数
263 class type2{
264     void pirnt(){}
265 };
266 class type{
267     friend class type2::print();
268 }; 

 

标签:const,函数,int,对象,初探,test,primer,指针
来源: https://www.cnblogs.com/matt-su/p/16247505.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有