constキーワードの理解
11028 ワード
const
キーワードのc/c++での役割は、修飾された変数を可変に限定することであり、初期化後は変更できません.const
修飾を変更しようとする変数の動作は、コンパイルエラーを引き起こします.const
の修飾規則は複雑であり,以下のコードを参照して理解できる.// NOTE: , , 。
const int a; // , 'a'
const int b = 15; //
int c;
const int *p; // , p const int , p , const int 。
p = &b; //
int const *q; // , q q ,
q = &c; // , q const int , int , q c 。
*q = 12; // , p 。
int * const x; // , 'x' ,x , int , 。
int * const y = &b; // , y int , const int , y b 。
int * const z = &c; // , z c , z 。
const int * const u = &b; // , u b, u , u b 。
const
がポインタを修飾しているのかタイプを修飾しているのか分からない場合は、const
と*
の位置から判断することができます.const
が*
の左側にある場合、修飾はタイプであり、const int * p
およびint const * q
のうちconst
はいずれもint
を修飾するために用いる.const
が*
の右側にある場合、修飾はポインタであり、int * const y
のconst
は修飾ポインタである.const
修飾ポインタによってconst
キーワードの意味が理解され、const
キーワードがどのような内容を修飾できるかを見てみましょう.const
は変数を修飾し、参照変数を修飾し、関数のパラメータを修飾し、関数の戻り値を修飾し、クラスのメンバー変数を修飾し、クラスのメンバー関数を修飾します.const修飾変数
int a = 15;
const int b = a; // , a b,b 。
const int c = b; // , b c, c 。
int d = c; // , c d.
b = c; // , b
c = d; // , c
const修飾参照
int a = 27;
const int &b = a; // ,b const int &, :b a , b a, b a。
int &c = a; // ,c int &, :c a ( ), c a, c a。
c = 12; // ,a 12, b 12
b = c; // ,b , 。
const int d = c; // ,d const int , 12,
const int &e = d; // ,e d 。
int &f = d; // , const int int & 。
// f , d , d f , d f , 。
int &g; // ,
const int &h; // ,
const修飾関数のパラメータ
関数のパラメータを理解することは,関数呼び出しを把握する上で非常に重要な一環である.最も重要な概念は、関数呼び出しで使用される値で渡すか、参照で渡すかを理解することです.
値別転送(by value)/参照別転送(by reference)
#include
void swap(int a, int b) //a、b
{
int temp = a;
a = b;
b = temp;
}
int mian()
{
int x = 1;
int y = 2;
swap(x,y); //x、y
std::cout << "x = " << x << " y = " << y << std::endl;
}
// ,
// x = 1,
// y = 2,
// a = x,( a x , a x )
// b = y,( b y , b y )
// temp = a,(temp = 1)
// a = b,(a = 2)
// b = temp,(b = 1)
// ,a b , 。
// x = 1, y = 2,
以上のコードが予想された結果を得られなかったのは,値で伝達された後,パラメータが実パラメータのコピーであり,パラメータの修正と実パラメータには何の関係もないためである.したがって、上記のコードは
a
およびb
の値を交換しただけであり、x
およびy
の値は変化しなかった.参照で渡すように変更します.次のように変更します.void swap(int &a, int &b)
{
int temp = a;
a = b;
b = temp;
}
const修飾関数パラメータ
値別転送と参照別転送の違いを理解したら、
const
キーワードが関数パラメータを修飾するときにコードに与える影響を見てみましょう.次に、参照によって渡される例を示します.int abs_good(const int& x)
{
if (x < 0) { return -x;}
else { return x;}
}
int abs_bad(int& x)
{
if (x < 0) { return -x;}
else { return x;}
}
// 。
int main()
{
int a = -15;
const int b = -7;
int c = abs_good(a); //c = 15;
int d = abs_bad(a); //d = 15;
int e = abs_good(b); //e = 7;
int f = abs_bad(b); // , b const int, 。
// abs_bad b ,
// abs_bad b ( , ), 。
}
上記の参照伝達例から分かるように、関数のパラメータを変更する必要がない場合、パラメータを宣言する際に説明すると、呼び出しの最大互換性が保証され、
int
型パラメータとconst int
型パラメータが呼び出されることができる.同時に、コードの可読性とセキュリティがより保障されています.次に、値別に渡す例を示します.
int abs_good(const int x)
{
if (x < 0) { return -x;}
else { return x;}
}
int abs_normal(int x)
{
if (x < 0) { return -x;}
else { return x;}
}
// 。
int main()
{
int a = -15;
const int b = -7;
int c = abs_good(a); //c = 15;
int d = abs_normal(a); //d = 15;
int e = abs_good(b); //e = 7;
int f = abs_normal(b); //f = 7; ,abs_normal x b , x b
}
上記の値による伝達の例から,値による伝達過程において,パラメータと実パラメータはそれぞれ独立していることが分かる.
const
キーワードを値で渡す必要はなく、関数呼び出しの互換性にも影響しません.しかし、パラメータを修正する必要がない場合は、const
キーワードを追加し、コードの可読性を高めると同時に、コンパイラにチェックしてもらい、無意識に修正していないことを確認することをお勧めします.関数パラメータがポインタの場合、参照による伝達と同様に、以下の簡単な例では、自己体験します.#include
void swap(int* pa, int* pb)
{
int temp = *pa;
*pa = *pb;
*pb = temp;
}
int abs_good(const int * x)
{
if (*x < 0) { return -*x; }
else { return *x; }
}
int main()
{
int a = 12;
int b = -22;
swap(&a, &b);
const int c = abs_good(&a);
std::cout << "a = " << a << " b = " << b << " c = " << c << std::endl;
}
const修飾関数戻り値
関数の戻り値が値によって渡される場合、
const
のキーワード修飾を使用しても効果はありません.関数の戻り値が参照によって渡される場合にのみ、const
のキーワードを使用する必要があります.ブレンドがc/c++でプログラミングされている場合、よく遭遇するシーンのタイプは次のとおりです.#include
#include
//
void print_message_bad(char *msg)
{
printf("Message[%s]
", msg);
}
int main()
{
std::string Info = "hello world";
print_message_bad(Info.c_str()); // ,"const char *" "char *"
return 0;
}
以上のコードコンパイルが失敗したのは、
std::string
のメンバー関数c_str()
の戻り値がconst char *
タイプであり、char *
タイプのパラメータに渡すことができないためである.その理由は、std::string
のタイプでは、ポインタが指すメモリの内容を直接変更することは禁止されており、ポインタが指すメモリの内容のみを読み取ることができます.std::string
型のsize()
やlength()
などの関数は、コンテンツ修正時に相応の変更が必要であるため、std::string
型で提供されるインタフェースを使用することができ、そうでなければstd::string
の動作の異常を引き起こす.#include
const int kMessageLen = 64;
const char* get_message(int line)
{
char *p = new char[kMessageLen];
snprintf(p, kMessageLen, "Message line :[%d]", line);
return p;
}
int main()
{
int line = 15;
const char *msg = get_message(line); //
printf("%s
", msg);
char *msg_error = get_message(line); // ,
return 0;
}
以上、
const
が戻りポインタを修飾する場合を説明し、次にconst
が戻り参照を修飾する場合を示す.const int& get_const_reference(const int & x)
{
return x;
}
int main()
{
int a = 15;
int b = get_const_reference(a);
const int c = get_const_reference(b);
//??? ? ?
a = 17;
b = 18;
c = 19;
//??? a b c 19 ?
int &d = get_const_reference(a);
const int &e = get_const_reference(c);
//??? ? ?
return 0;
}
さて、上記の例では、
const
キーワードが戻り関数の戻り値を修飾する役割が理解されているはずです.この使用は、クラスのメンバー関数によく使用されます.クラスが内部データにアクセスし、内部データを保護することを望んでいる場合は、const
で修飾された関数の戻り値で完了できます.const修飾クラスメンバー変数
#include
#include
class Student{
public:
explicit Student(std::string name, int age): age_(age), name_(name)
{}
~Student() {};
int get_age() { return age_; }
const std::string& get_name() { return name_; }
private:
int age_;
const std::string name_; // ??? const ?
};
int main()
{
Student xiaoMing("xiaoMing", 8);
Student xiaoHong("xiaoHong", 7);
std::cout << "name: " << xiaoMing.get_name() << " age: " << xiaoMing.get_age() << std::endl;
std::cout << "name: " << xiaoHong.get_name() << " age: " << xiaoHong.get_age() << std::endl;
return 0;
}
const
クラスのメンバー変数を修飾する場合、宣言時に初期化する必要はなく、コンストラクション関数で初期化する必要があります.このルールは、クラスオブジェクトがインスタンス化(構造/存在)されている場合にのみ、const
で修飾されたクラスのメンバー変数がインスタンス化(構造/存在)を開始することを理解しやすい.実際には、c++11以降でクラスのメンバー変数に宣言時に値を割り当てることができます.この値はデフォルト値として使用できます.コンストラクション関数に対応する値が指定されていない場合に使用されます.詳細:「C++11の詳細理解」を参照して、次のコードを参照してください.// NOTE: c++98
#include
#include
class Student{
public:
Student() {}
Student(std::string name, int age): age_(age), name_(name)
{}
~Student() {};
int get_age() { return age_; }
const std::string& get_name() { return name_; }
private:
int age_ = 165;
const std::string name_ = "gua";
};
int main()
{
Student xiaoMing("xiaoMing", 8);
Student oldMan;
std::cout << "name: " << xiaoMing.get_name() << " age: " << xiaoMing.get_age() << std::endl;
std::cout << "name: " << oldMan.get_name() << " age: " << oldMan.get_age() << std::endl;
return 0;
}
次はmuduoネットワークライブラリのTimestampのコードセグメントです.
static const int kMicroSecondsPerSecond = 1000 * 1000;
に注意してください.class Timestamp :
{
public:
Timestamp()
: microSecondsSinceEpoch_(0)
{
}
// default copy/assignment/dtor are Okay
string toString() const;
string toFormattedString(bool showMicroseconds = true) const;
bool valid() const { return microSecondsSinceEpoch_ > 0; }
static const int kMicroSecondsPerSecond = 1000 * 1000; // c++98 c++11 。 ?
private:
int64_t microSecondsSinceEpoch_;
};
class A {
const static int num1; //
const static int num2 = 13; //
};
const int A::num1 = 12; // ,
const int num2; //
const修飾クラスメンバー関数
const
修飾クラスのメンバー関数クラスを設計する際、重要な原則は、データメンバー関数を変更しない場合にconst
を追加し、データメンバーを変更するメンバー関数にconst
を追加しないことです.したがって、const
キーワードは、メンバー関数の動作をより明確に限定します.(1)
const
で修飾されたメンバー関数(関数の前やパラメータテーブル内ではなく、const
が関数パラメータテーブルの後ろに置かれていることを指す)は、データメンバーのみを読み取ることができ、データメンバーを変更することはできない.const
修飾メンバー関数はなく、データ・メンバーは読み書き可能である.(2)それ以外にクラスのメンバー関数にconstを付けるメリットは何ですか?すなわち、定数オブジェクト(すなわち、const
で修飾されたクラスオブジェクト)は、const
メンバー関数を呼び出すことができ、const
で修飾されていない関数を呼び出すことができない.詳細アクセス可能:C++のconstクラスメンバー変数、constメンバー関数