class and struct
目錄
- 前文
- 問題
- 物件與參考
- 參考的傳遞
- 物件 copy
- shallow copy
- depthcopy
- memcpy(data,a.data,sizeof(T) *n);
- 簡單型別
- 復雜型別
- 指標型別的拷貝
- 原位構造
- 回傳值優化 (RVO)
- 拷貝構造的現象
- 關鍵字
- default
- delete
- 函式多載
- 問題
- code
前文
c++ 中的class 和struct
struct 默認為 public
class 默認為private
問題
左值參考,右值參考區別?
什么時候需要定值淺拷貝和深拷貝的函式,
傳值和傳值的區別
物件與參考
參考的傳遞
回傳參考就意味著是data[ind]的別名,
using namespace std;
class Vector{
public:
Vector(int n =100) : n(n),data(new int[n]){}
int &at(int ind){return data[ind];}
private :
int n ;
int *data;
};
int main(){
Vector arr;
for(int i=0; i< 1; i++){
arr.at(i) = i;
}
return 0;
}
物件 copy
實際上兩種copy是邏輯上的
class Vector{
public:
Vector(int n =100) : n(n),data(new int[n]){}
int &at(int ind){
cout << "ind" << ind << endl;
return data[ind];
}
int &operator[](int ind) {return data[ind];}
void output (int m = -1){
if(m == -1) m = n;
cout <<"Arr" << this << endl;
for(int i = 0; i < m; i++){
cout << data[i] << " ";
}
cout << endl;
return ;
}
private :
int n ;
int *data;
};
shallow copy
int main(){
Vector arr;
for(int i=0; i< 10; i++) arr[i]=i;
arr.output(10);
Vector arr2(arr);
arr2.output(10);
arr2[3]=1000;
arr.output(10);
arr2.output(10);
return 0;
}
Arr0x7ffcddc92630 arr
0 1 2 3 4 5 6 7 8 9
Arr0x7ffcddc92620 arr 2
0 1 2 3 4 5 6 7 8 9
Arr0x7ffcddc92630
0 1 2 1000 4 5 6 7 8 9
Arr0x7ffcddc92620
0 1 2 1000 4 5 6 7 8 9
arr {n=100 data=https://www.cnblogs.com/yijieyufu/archive/2023/03/18/0x000002196c4ad400 {0} } Vector
arr2 {n=100 data=0x000002196c4ad400 {0} } Vector
vs 的debug 模式下 data
本代碼將arr 拷貝給了arr2 ,本身屬于兩個不同的物件,
既然屬于不同的物件,arr2的變動不應對arr1發生改變,但是事與愿違 ,因為受拷貝行為的影響,依次性的拷貝,由于是指標域,拷貝了同一個存盤區的值,所以 arr2 發生更改,arr1 也會發生更改,
?如果copy 的物件是指標型別時,這個方式就無法適用了.
depthcopy
指標型別并非是賦值關系,存arr2 應該是一個額外的存盤區,
memcpy(data,a.data,sizeof(T) *n);
當前方法在一定的簡單型別的場景下可以進行深拷貝,如果型別中存在了指標型別則也會出現兩個指標指向同一個地址的的情況,
#include<iostream>
#include<vector>
#include<algorithm>
#include<cstring>
using namespace std;
#define BEGINS(x) namespace x {//begin of namespace
#define ENDS(x)} //end of namespace x
BEGINS(xxx)
class A {
public :
int x , y;
};
ostream &operator<<(ostream &out ,const A &a){
out << "(" << a.x << " , " << a.y << ")";
return out;
}
template<typename T>
class Vector{
public:
Vector(int n =100) : n(n),data(new T[n]){}
Vector (const Vector &a): n(a.n){
// 實作深拷貝
data = https://www.cnblogs.com/yijieyufu/archive/2023/03/18/new T[n];
/*
for(int i = 0; i < n; i++){
data[i]=a.data[i];
} */
//實作以上的效果
//1 : memcpy 可能是復制型別是復雜型別的物件
memcpy(data,a.data,sizeof(T) *n);
return ;
}
T &at(int ind){
cout <<"ind" << ind << endl;
return data[ind];
}
T &operator[](int ind) {return data[ind];}
void output (int m = -1){
if(m == -1) m = n;
cout << "Arr size " << sizeof(data) << " " << this << endl;
for(int i = 0; i < m; i++){
cout << data[i] << " ";
}
cout << endl;
return ;
}
private :
int n ;
T *data;
};
ENDS(xxx)
簡單型別
BEGINS(test1)
using namespace xxx;
int main(){
Vector<int> arr;
for(int i=0; i< 10; i++) arr[i]=i;
arr.output(10);
Vector<int> arr2(arr);
arr2.output(10);
arr2[3]=1000;
arr.output(10);
arr2.output(10);
return 0;
}
ENDS(test1)
Arr size 8 0x7ffc86e546d0
0 1 2 3 4 5 6 7 8 9
Arr size 8 0x7ffc86e546c0
0 1 2 3 4 5 6 7 8 9
Arr size 8 0x7ffc86e546d0
0 1 2 3 4 5 6 7 8 9
Arr size 8 0x7ffc86e546c0
0 1 2 1000 4 5 6 7 8 9
?由結果可以看到,很顯然在copy后并沒有影響arr1的指標域
復雜型別
BEGINS(test2)
using namespace xxx;
int main(){
Vector<A> arr1;
for(int i = 0; i < 10; i++){
arr1[i].x = i;
arr1[i].y = 2 * i;
}
arr1.output(10);
Vector<A> arr2(arr1);
arr2[3] = (A){4, 100};
arr2.output(10);
arr1.output(10);
return 0;
}
ENDS(test2)
Arr size 8 0x7ffc053fdeb0 //原
(0 , 0) (1 , 2) (2 , 4) (3 , 6) (4 , 8) (5 , 10) (6 , 12) (7 , 14) (8 , 16) (9 , 18)
Arr size 8 0x7ffc053fdea0
(0 , 0) (1 , 2) (2 , 4) (4 , 100) (4 , 8) (5 , 10) (6 , 12) (7 , 14) (8 , 16) (9 , 18)
Arr size 8 0x7ffc053fdeb0
(0 , 0) (1 , 2) (2 , 4) (3 , 6) (4 , 8) (5 , 10) (6 , 12) (7 , 14) (8 , 16) (9 , 18)
?在不涉及物件中的(指標型別)(其中型別不需要深拷貝的場景)的情況下,可以實作深拷貝,
指標型別的拷貝
memcpy 是無法做到深拷貝的,無法完美的遷移,
BEGINS(test3)
using namespace xxx;
int main(){
Vector<Vector<int>> arr1;
Vector<Vector<int>> arr2(arr1);
arr2[2][2]=1000;
for(int i =0; i <3 ; i++){
arr1[i].output(3);
cout <<endl;
}
for(int i =0; i <3 ; i++){
arr2[i].output(3);
cout <<endl;
}
return 0;
}
ENDS(test3)
Arr size 8 0xbe4680
0 0 0
Arr size 8 0xbe4690
0 0 1000
Arr size 8 0xbeef40
0 0 0
Arr size 8 0xbeef50
0 0 0
Arr size 8 0xbeef60
0 0 1000
原位構造
位構造 new (地址),其意思為:在某個地址上呼叫某個建構式,構造一個相關的物件
如果資料型別T執行深拷貝的函式,將call T型別的拷貝建構式
https://hedzr.com/c++/variant/in-place-construction-in-cxx/ 參考文章
Vector(const Vector &a):n(a.n){
// data = https://www.cnblogs.com/yijieyufu/archive/2023/03/18/new T[n];//實作深拷貝, 帶new 就呼叫n次建構式
data =(T *)malloc(sizeof(T) * n);
/* for(int i = 0; i < n; i++){
data[i]=a.data[i];
} */
//1 : memcpy 可能是復制型別是復雜型別的物件
//原位構造 new (地址),其意思為:在某個地址上呼叫某個建構式,構造一個相關的物件
for(int i = 0; i < n; i++){
//如果資料型別T執行深拷貝的函式,將call T型別的拷貝建構式
new (data + i) T(a.data[i]); //這里也調了i次
}
// memcpy(data,a.data,sizeof(T) * n);
return ;
}
回傳值優化 (RVO)
物件: 已經經歷了構造程序的資料區 ,obj (申請物件資料區)→匹配建構式 →完成構造(某物件)
-fno-elide-constructors 關倍訓傳值優化
拷貝構造的現象
按照流程應該是發生了三次構造 ? 為什么輸出的解構為什么是同一個地址
按照正常邏輯, 中間的匿名物件應該沒意義的 只是一個中轉的功能
所以 第一步就吧匿名的臨時變數優化掉
第二 ->tmep 拷貝給A物件 ,既然是拷貝他們是一模一樣,加在temp物件所有的操作都會在去往A上
temp 物件其實可以看做A物件的別名,這就是回傳值優化的最終版本
最終是 吧temp 當做A物件的參考了
#include<iostream>
#include<queue>
#include<cmath>
#include<vector>
#include<set>
#include<map>
#include<stack>
#include<algorithm>
using namespace std;
class A{
public:
A(){
cout << this <<" default constructor" << endl;
}
A(int x){
cout << this << " param cpms" << endl;
}
A(const A&a){
cout << " copy constructor" << endl;
}
};
A func(){
A temp(3); // 代引數的構造
cout << " temp " << &temp << endl;
return temp;// 這里應該是屬于匿名變數拷貝給了 A
}
int main(){
A a =func(); //匿名物件在拷貝A
cout <<" a :" << &a <<endl;
return 0;
}
關閉的優化后
0x7ffc7872633f param cpms
temp 0x7ffc7872633f
copy constructor
copy constructor
a :0x7ffc7872635e
優化后
0x7ffef5b875cf param cpms
temp 0x7ffef5b875cf
a :0x7ffef5b875cf
關鍵字
隱形的行為,顯性化 ,將代碼的層面的規則,變成顯示的規則
default
不一樣點 :如果想要實作拷貝構造必須是初始化串列挨個拷貝, default 關鍵字會挨個執行拷貝方法
如果是默認的建構式,他是一個東西
class A{
A() = default; // 隱性的行為顯性化
A(const A &) =default;
A(const A &) {} //這和上面不是一個東西
};
delete
洗掉后,如果想使用,編譯會直接報錯,
class A{ |~
A() = default; // 隱性的行為顯性化 |~
//A(const A &) =default; |~
A(const A &) = delete; |~
|~
// A() {} 這和上面不是一個東西 |~
}; |~
函式多載
如果一個作用內,函式名稱相同,但是引數串列不同,稱函式多載 (引數型別不同,引數個數不同(記得默認引數的問題)),
強調一下 和回傳值沒關系
問題
函式多載 好處?
- 有了函式多載,可以吧把一類通過引數區分的功能,命名成一個函式名,(精細化的處理流程)
- 通過函式引數的串列對函式進行提示
- 對函式進行擴展
code
int func(int x ){
return 2 * x;
}
double func(double x){
return 2.0 * x;
}
int ABS(int x ){
return abs(x);
}
double ABS(double x){
return fabs(x);
}
int main(){
cout << func(1) << endl;
cout << func(22.0) << endl;
cout << ABS(222.56) << endl;
cout << ABS(-233) << endl ;
return 0;
}
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/547270.html
標籤:其他
